本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试。
记录日期:2022.1.21
大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习。
文章目录
框架原理 - Spring(一) 之 Spring AOP 引入
认识AOP
概念
AOP(Aspect Orient Programming
) 是一种思想,是面向对象编程的一种补充,是一种面向切面编程的思想,AOP的主要作用就是,告诉我们可以通过动态代理的方式,织入一段代码到已经写到另一段代码中去。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。
相关概念
Joinpoint
(连接点):在系统运行之前,AOP 的功能模块都需要织入到具体的功能模块中。要进行这种织入过程,我们需要知道在系统的哪些执行点上进行织入过程,这些将要在其之上进行织入操作的系统执行点就称之为 Joinpoint,最常见的 Joinpoint 就是方法调用。Pointcut
(切点):用于指定一组 Joinpoint,代表要在这一组 Joinpoint 中织入我们的逻辑,它定义了相应 Advice 将要发生的地方。通常使用正则表达式来表示。对于上面的例子,Pointcut 就是表示 “所有要加入日志记录的接口” 的一个 “表达式”。例如:“execution(* com.joonwhee.open.demo.service..*.*(..))”
。Advice
(通知/增强):Advice 定义了将会织入到 Joinpoint 的具体逻辑,通过 @Before、@After、@Around 来区别在 JointPoint 之前、之后还是环绕执行的代码。Aspect
(切面):Aspect 是对系统中的横切关注点逻辑进行模块化封装的 AOP 概念实体。类似于 Java 中的类声明,在 Aspect 中可以包含多个 Pointcut 以及相关的 Advice 定义。Weaving
(织入):织入指的是将 Advice 连接到 Pointcut 指定的 Joinpoint 处的过程,也称为:将 Advice 织入到 Pointcut 指定的 Joinpoint 处。Target
(目标对象):符合 Pointcut 所指定的条件,被一个或多个切面所织入 Advice 的对象。
Advice 类型
前置通知
(Before advice):在某连接点(join point)之前执行的通知,但不能阻止连接点前的执行(除非它抛出异常)。返回后通知
(After returning advice):在某连接点(join point)正常完成后执行的通知。抛出异常后通知
(After throwing advice):在方法抛出异常退出时执行的通知。后通知
(After(finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。环绕通知
(Around Advice):包围一个连接点(join point)的通知。
AOP 实现方式
静态织入
典型代表:AspectJ
。
AspectJ
AspectJ 是一个易用的功能强大的 AOP 框架。
AspectJ 全称是 Eclipse AspectJ
,截至目前最新版本为:1.9.7。
官方网址:The AspectJ Project | The Eclipse Foundation
引用官网描述:
- a seamless aspect-oriented extension to the Javatm programming language(一种基于Java平台的面向切面编程的语言)
- Java platform compatible(兼容Java平台,可以无缝扩展)
- easy to learn and use(易学易用)
可以单独使用,也可以整合到其它框架中。
单独使用 AspectJ 时需要使用专门的编译器 ajc,而java 的编译器是 javac,AspectJ 的编译器是 ajc,aj 是首字母缩写,c 即 compiler。
动态织入(JDK动态代理、CGLib动态代理)
典型代表:SpringAOP
、JbossAOP
。
SpringAOP
SpringAOP 是借用了 AspectJ 的配置语法,现在直接使用AspectJ做动态代理的项目很少了,都是直接使用spring,spring内置了 AspectJ。
JbossAOP
不了解。
SpringAOP 和 AspectJ 的区别?
Spring AOP
- 基于动态代理来实现,默认如果使用接口的,用JDK提供的动态代理实现,如果是方法则使用CGLIB实现。
- Spring AOP 需要依赖 IOC 容器来管理,并且只能作用于Spring容器,使用纯Java代码实现。
- 在性能上,由于 Spring AOP 是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得Spring AOP 的性能不如AspectJ的那么好。
AspectJ
-
AspectJ来自于Eclipse基金会。
-
AspectJ属于静态织入,通过修改代码来实现,有如下几个织入的时机:
-
编译期织入(Compile-time weaving): 如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。
-
编译后织入(Post-compile weaving): 也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。
-
类加载后织入(Load-time weaving): 指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法:
- 自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就可以在加载的时候定义行为了
- 在 JVM 启动的时候指定 AspectJ 提供的 agent:
-javaagent:xxx/xxx/aspectjweaver.jar
。
-
-
AspectJ可以做 Spring AOP 干不了的事情,它是AOP编程的完全解决方案,Spring AOP则致力于解决企业级开发中最普遍的AOP(方法织入),而不是成为像AspectJ一样的AOP方案。
-
因为AspectJ在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的。
使用Spring AOP
导入依赖
使用 Spring AOP 时,我们需要引入两个包,以SpringBoot项目来说,当我们引入 Spring-boot-starter-aop
包时,里面就间接引用这两个包:
spring-aop
:是Spring对 AOP 的具体实现的包。aspectjweaver
:是spring的切入点表达式需要用的包。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
创建接口和实现类
接口代码:
public interface personServer
{
public void save(String uname,int age);
}
实现类代码:
@Service
public class personServerImpl implements personServer {
@Override
public void save(String uname,int age) {
//打开下面两行引起报错,可触发异常通知
int a = 0;
age = age / a;
System.out.println("come in personServerImpl save method...");
}
}
创建切面类
然后我们来编写具体AOP切面类:
@Aspect //声明这是一个切面
@Component
public class AspectIntercepter {
@Pointcut(value = "execution(* com.test.service.personServerImpl.*(..))")
private void pointCut(){
// 定义一个切入点 后面的通知直接引入切入点方法pointCut即可
// personServerImpl下面的所有方法
}
//环绕通知(连接到切入点开始执行,下一步进入前置通知,在下一步才是执行操作方法)
@Around(value="pointCut()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("@Around进入环绕通知...");
Object object = pjp.proceed(); //执行该方法
System.out.println(pjp.getThis() + "操作结束,退出方法;环绕[@Around]结束!...");
return object;
}
//前置通知(进入环绕后执行,下一步执行方法)
@Before(value = "pointCut()")
public void doAccessCheck(JoinPoint joinPoint) {
System.out.println("@Before前置通知:" + Arrays.toString(joinPoint.getArgs()));
}
//异常通知(出错时执行)
@AfterThrowing(value = "pointCut()",throwing="ex")
public void doAfterThrow(JoinPoint joinPoint,Throwable ex) {
System.out.println("@AfterThrowing例外通知(异常通知)" + Arrays.toString(joinPoint.getArgs()));
System.out.println("@AfterThrowing异常信息:" + ex);
}
//后置通知(返回之前执行)
@After(value = "pointCut()")
public void after() {
System.out.println("@After后置通知...");
}
//最终通知(正常返回通知,最后执行)
@AfterReturning(value = "pointCut()")
public void doAfter() {
System.out.println("@AfterReturning最终通知...End!");
}
}
注意点
Spring 4 和 Spring 5中AOP的执行顺序
在Spring 4 和 Spring 5中AOP的执行顺序区别:Spring4和Spring5的AOP顺序