🤖 AOP
2022年6月20日
- frame
🤖 AOP
1. 是什么?
- AOP - 面向切面编程
- 将一些业务逻辑中的相同代码抽取到一个独立的模块中,让业务逻辑更加简洁
- AOP 可以在 编译期间、类加载期间、运行期间 实现在不修改 源代码 的情况下给程序
动态
添加功能 - AOP 的核心是 动态代理
- AOP 主要应用于处理一些具有横切性质的系统级服务
- 日志收集
- 事务管理
- 安全检查
- 缓存
- 对象池管理
2. 简单案例
3. 核心概念
- 切面 [Aspect]
- 作用于类上,表示这个类是一个切面
@Aspect
- 切点 [Pointcut]
- 定义要拦截的 bean
- 正则表达式,当有连接点匹配到该表达式时,将触发相关通知,可以定位到 类、方法、某个注解 等
@PointCut(execution(public *.com.example.controller.*.*(..)))
- 连接点 [Join Point]
- 定义要拦截的 bean 的哪个方法
- 程序中执行的特定点,例如方法执行、异常处理等
- Spring 只支持 方法 的连接点,实际上 连接点 还可以是 属性 | 构造器
- 通知 [Advice]
- 环绕通知
@Around(value = "methodA()"
- 前置通知
@Before(value = "methodA()"
- 后置通知
@After(value = "methodA()"
- 异常通知
@AfterThrowing(value = "methodA()"
- 返回通知
@AfterReturning(value = "methodA()"
- 环绕通知
- 织入 [Weaving]
- 将 增强 添加到 目标类 的具体连接点上的过程
- 编译期织入
- 目标类由
.java
编译成.class
时织入 AspectJ
可以采用该方式
- 目标类由
- 类加载期织入
- 切面在目标类加载到 JVM 时织入,需要特殊的 类加载器,它可以在目标类被引入使用前 增强到该目标类的 字节码
AspectJ
可以采用该方式
- 运行期织入
- 切面在应用运行的某个时刻被织入
- 一般情况下,在织入切面时,AOP 容器 会为目标对象 动态 地创建一个代理对象
Spring AOP
以该种方式织入
- 编译期织入
- 将 增强 添加到 目标类 的具体连接点上的过程
4. 执行流程
- 多个切面的情况下,可以通过
@Order
指定先后顺序- 数字越小,优先级越高
5. 代理
1) 静态代理
- 由程序员创建代理类或特定工具自动生成源代码再对其编译
- 在编译时已经将接口,被代理类(委托类),代理类等都被确定下来,在程序运行前代理类的.class文件已经生成
- 缺点:
- 一个代理类只能实现一个 委托类,虽然可以 implements 多个接口,但违背单一职责原则,所以要代理多个委托类,就需要写多个代理类,浪费时间和精力
代码实现:
2) 动态代理
- 在程序运行后通过反射创建生成字节码再由 JVM 加载而成
- Spring AOP 中的动态代理主要有两种方式:
- JDK动态代理
- 只能代理实现了接口的类
- 通过与委托类实现同样的接口,然后在实现的接口方法里进行增强来实现的
- CGLIB动态代理
- 可以代理未实现任何接口的类
- 不能代理声明为
final
类型的类和方法,因为它是通过生成一个被代理类的子类来拦截被代理类的方法调用 private
方法不能被继承,final
方法不能被重写,static
方法和继承不相干
- JDK动态代理
代码实现 - JDK 动态代理
代码实现 - CGLib 动态代理
- 底层原理:
- 通过字节码增强技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
- 由于反射的效率比较低,所以 CGlib 采用了
FastClass
的机制来实现对被拦截方法的调用 - FastClass 机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法
- GCLib 创建的 代理对象 性能高,但是 代理对象的创建过程 比 JDK 要长
- 对于无需频繁创建的对象,例如 单例对象,采用 GCLib 合适;反之,采用 JDK 更合适
6. Spring AOP VS AspectJ AOP
Spring AOP | AspectJ AOP |
---|---|
运行期 织入 | 编译期 织入、编译后 织入、类加载后 织入 |
基于 动态代理 实现 | 静态织入,通过 修改代码 实现 |
只支持 方法 的织入 | 可增强 构造函数 、类静态变量 、字段 、方法 |
只支持 Spring IoC 管理的 bean | 所有 类 |
性能较慢 | 性能快,代理类无额外运行开销,在运行前已经完成织入工作 |
javac 编译器 | 需要单独的 编译器 ajc |
提供最普通的 AOP | 提供功能强大的 AOP 完全解决方案 |
编译期
织入- 对
.java
进行代码增强
- 对
编译后
织入- 对已经生成
.class
或.jar
文件进行代码增强
- 对已经生成
类加载后
织入- 在加载类时 进行织入