一、认识Spring AOP
1.1 什么是AOP
AOP:全称Aspect Oriented Programming,面向切面编程,它是对某一类事情的几种处理。
AOP是一种思想,而Spring AOP则是对AOP思想的具体实现。
1.2 AOP的组成
1.2.1 切面(Aspect)
切面就是包含了切点和通知的一个类,相当于AOP实现某个功能的集合。
1.2.2 切点(Pointcut)
切点是切面中的一个方法,它制定了AOP进行主动拦截请求的规则,即AOP需要拦截什么样的请求。
1.2.3 通知(Advice)
通知描述了AOP需要在什么时候做什么事情,它是切面中的一个方法。
在切面类中,可以在方法上添加以下注解,将方法设置为通知方法,在满足条件时执行该通知方法:
前置通知@Before:在目标方法调用之前执行。
后置通知@After:在目标方法调用之后执行。
返回之后通知@AfterReturning:在目标方法返回后调用(即执行完return语句后)。
抛异常后通知@AfterThrowing:在目标方法抛出异常后调用。
环绕通知@Around:在目标方法调用之前和调用之后都会执行。
1.2.4 连接点(Join Point)
可能会触发切点中制定的规则的请求就是连接点。
1.3 AOP的作用
对于一些功能统一,且使用的地方比较多的功能,比如登录验证功能,就可以使用AOP来统一处理。除此之外,AOP还可以实现统一日志记录,统一方法执行时间统计,统一的返回格式设置,统一的异常处理,事务的开启和提交等。
二、Spring AOP的使用
2.1 添加AOP框架支持
在Spring Boot框架中的pom.xml文件中添加AOP依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.9</version>
</dependency>
2.2 定义切面和切点
切点是一个空方法,不需要有具体实现,用于起到“标识”的作用,标识通知方法具体指的是哪个切点(切点可以有多个)。
切点表达式说明:
AspectJ支持三种通配符:
*:匹配任意字符,只匹配一个元素(包、类、方法或方法参数);
..:匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用;
+:表示按照类型匹配指定类的所有子类,例如com.example.demo.Controller+,表示匹配Controller类及其所有子类。
切点表达式由切点函数组成,execution()是最常用的切点函数,用来匹配方法,语法为:
execution(<修饰符><返回类型><包.类.方法(参数)> <异常>)
其中,修饰符和异常可以省略。
2.3 定义通知
此时,调用UserController类中所有方法的请求都会在特定时机执行特定通知方法。
三、Spring AOP的实现原理
Spring AOP是构建在动态代理基础上,Spring AOP支持JDK Proxy和CGLIB方式实现动态代理。默认情况下,实现了InvocationHandler接口的类,使用AOP会基于JDK生成代理类,没有实现InvocationHandler接口的类,会基于CGLIB生成代理类。
3.1 织入
织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入:
(1) 编译期:切面在目标类编译时被织入。AspectJ的织入编译器就是以这种方式织入切面的。
(2) 类加载期:切面在目标类加载到JVM时被织入。
(3) 运行期:切面在应用运行的某一时刻被织入。Spring AOP就是以这种方式织入切面的。
3.2 JDK和CGLIB实现的区别
(1) JDK实现,要求被代理类必须实现InvocationHandler接口,之后是通过Proxy类,在运行时动态的在内存中生成了代理类对象,该代理对象是通过实现同样的接口实现,只是该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成。
(2) CGLIB实现,被代理类可以不实现InvocationHandler接口,是通过继承被代理类,在运行时动态的生成代理类对象。