目录
1.什么是 Spring AOP?
AOP(Aspect Oriented Programming):面向切面编程,它是一种思想,它是对某一类事情的集中处理。
AOP的诞生不是为了替代OOP,AOP相对于OOP来说,是一个补充的关系,不是一个替代的关系。
AOP 是一种思想, Spring AOP 是一个框架,提供了一种对 AOP 思想的实现,它们的关系和 IoC 与 DI 类似。
2.为什么要用AOP?
- 统一的用户登录判断
AOP 还可以实现:
- 统一日志记录
- 统一方法执行时间统计
- 统一的返回格式设置
- 统一的异常处理
- 事务的开启和提交等
使用AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP(Object Oriented Programming,面向对象编程)的补充和完善。
3.Spring AOP 应该怎么学习呢?
Spring AOP 学习主要分为以下 3 个部分:
- 学习 AOP 是如何组成的?也就是学习 AOP 组成的相关概念。
- 学习 Spring AOP 使用。
- 学习 Spring AOP 实现原理。
3.1 AOP 组成
3.1.1 切面(Aspect)
切面(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包括了连接点的定义。
切面是包含了:通知、切点和切面的类,相当于 AOP 实现的某个功能的集合。
定义AOP的业务类型(要干嘛:要验证登录?要记录日志?要统计方法执行时间?)
3.1.2 连接点(Join Point)
应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常时,甚 修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
连接点相当于需要被增强的某个 AOP 功能的所有方法。
AOP中的所有方法
3.1.3 切点(Pointcut)
Pointcut 是匹配 Join Point 的谓词。
Pointcut 的作用就是提供一组规则(使 AspectJ pointcut expression language 来描述)来匹配 Join Point,给满足规则的 Join Point 添加 Advice。
切点相当于保存了众多连接点的一个集合(如果把切点看成一个表, 连接点就是表中一条一条的数据)。
提供一组规则,用来匹配连接点
3.1.4 通知(Advice)
切面也是有目标的 ——它必须完成的工作。在 AOP 术语中,切面的工作被称之为通知。
通知:定义了切面是什么,何时使用,其描述了切面要完成的工作,还解决何时执行这个工作的问题。
提供AOP方法实现
5个通知类型:
- 前置通知:使用@Before:通知方法会在目标方法调用之前执行。
- 后置通知:使用@After:通知方法会在目标方法返回或者抛出异常后调用。
- 异常返回之后通知:使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。
- return之后通知:使用 @AfterReturning:通知方法会在目标方法返回后调用。
- 环绕通知:使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。
切点相当于要增强的方法。
AOP 整个组成部分的概念如下图所示,以多个页面都要访问用户登录权限为例:
可以这样通俗理解:
切面相当于一个俱乐部,连接点为想进去俱乐部的用户,而切点是一个VIP条件,它规定了VIP用户可以进入俱乐部,而非VIP的用户不可以进入这个俱乐部;通知就是会员在俱乐部里面可以玩什么(例如三国杀、剧本杀等,总共5类)
3.2 Spring AOP 实现
接下来我们使用Spring AOP 来实现一下 AOP 的功能,完成的目标是拦截所有UserController 的方法,每次调 UserController 中任意一个方法时,都执行相应的通知事件。
Spring AOP 的实现步骤如下:
- 添加 Spring AOP 框架支持。
- 定义切面和切点。
- 定义通知。
3.2.1 添加 AOP 框架支持
创建Spring Boot项目;
Maven中央仓库
选择和自己创建Spring Boot版本相同的版本号:
我的为Spring Boot 2.6.10
在 pom.xml 中添加如下配置:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.10</version>
</dependency>
记得添加完成后点击导入!!
3.2.2 定义切面和切点
切点指的是具体要处理的某一类问题,比如用户登录权限验证就是一个具体的问题,记录所有方法的执行日志就是一个具体的问题,切点定义的是某一类问题。
a) 定义切面:
b) 定义切点
切点表达式说明:
execution(<修饰符><返回类型><包.类.方法(参数)><异常>)
表达式示例:
execution(* com.cad.demo.User.* (…)) :匹配 User 类里的所有方法。
execution(* com.cad.demo.User+.* (…)) :匹配该类的子类包括该类的所有方法。
execution(* com.cad.* .* (…)) :匹配 com.cad 包下的所有类的所有方法。
execution(* com.cad…* .* (…)) :匹配 com.cad 包下、子孙包下所有类的所有方法。
execution(* addUser(String, int)) :匹配 addUser 方法,且第一个参数类型是 String,第二个参数类型是 int。
3.2.3定义通知
5个通知类型:
- 前置通知:使用@Before:通知方法会在目标方法调用之前执行。
- 后置通知:使用@After:通知方法会在目标方法返回或者抛出异常后调用。
- 异常返回之后通知:使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。
- return之后通知:使用 @AfterReturning:通知方法会在目标方法返回后调用。
- 环绕通知:使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。
前置通知:
后置通知:
异常返回之后通知:
返回值通知:
后置通知@After执行在@AfterReturning之后
环绕通知:
3.3 Spring AOP 实现原理
Spring AOP 是构建在动态代理基础上,能够代理的最小单位为方法。
Spring AOP代理实现方式:
- JDK Porxy(JDK动态代理)
- GGLIB(基于字节码的动态代理)
默认情况下,实现了接口的类,使用AOP 会基于 JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。
3.3.1织入(Weaving):代理的生成时机
Sping AOP是在程序运行期织入代理对象。
织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入:
JDK 和 CGLIB 实现的区别
- JDK 实现,要求被代理类必须实现接口 ,之后是通过
InvocationHandler
及Proxy
,在运行时动态的在内存中生成了代理类对象,该代理对象是通过实现同样的接口实现(类似静态代理接口实现的方式),只是该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成。
2.CGLIB 实现,被代理类可以不实现接口,是通过继承被代理类,在运行时动态的生成代理类对象。
总结
AOP 是对某方面能力的统一实现,它是一种实现思想,Spring AOP 是对 AOP 的具体实现,Spring AOP 可通过 AspectJ(注解)的方式来实现 AOP 的功能,Spring AOP 的实现步骤是:
- 添加 AOP 框架支持。
- 定义切面和切点。
- 定义通知。
Spring AOP 是通过动态代理的方式,在运行期将 AOP 代码织入到程序中的,它的实现方式有两种:JDK Proxy 和 CGLIB。