🤖 AOP

吞佛童子2022年6月20日
  • frame
  • Spring
  • AOP
大约 7 分钟

🤖 AOP

1. 是什么?

  • AOP - 面向切面编程
  • 将一些业务逻辑中的相同代码抽取到一个独立的模块中,让业务逻辑更加简洁
  • AOP 可以在 编译期间类加载期间运行期间 实现在不修改 源代码 的情况下给程序动态添加功能
  • AOP 的核心是 动态代理
  • AOP 主要应用于处理一些具有横切性质的系统级服务
    • 日志收集
    • 事务管理
    • 安全检查
    • 缓存
    • 对象池管理

2. 简单案例

3. 核心概念

  1. 切面 [Aspect]
    • 作用于上,表示这个类是一个切面
    • @Aspect
  2. 切点 [Pointcut]
    • 定义要拦截的 bean
    • 正则表达式,当有连接点匹配到该表达式时,将触发相关通知,可以定位到 方法、某个注解
    • @PointCut(execution(public *.com.example.controller.*.*(..)))
  3. 连接点 [Join Point]
    • 定义要拦截的 bean 的哪个方法
    • 程序中执行的特定点,例如方法执行、异常处理等
    • Spring 只支持 方法 的连接点,实际上 连接点 还可以是 属性 | 构造器
  4. 通知 [Advice]
    • 环绕通知
      • @Around(value = "methodA()"
    • 前置通知
      • @Before(value = "methodA()"
    • 后置通知
      • @After(value = "methodA()"
    • 异常通知
      • @AfterThrowing(value = "methodA()"
    • 返回通知
      • @AfterReturning(value = "methodA()"
  5. 织入 [Weaving]
    • 将 增强 添加到 目标类 的具体连接点上的过程
      • 编译期织入
        • 目标类由 .java 编译成 .class 时织入
        • AspectJ 可以采用该方式
      • 类加载期织入
        • 切面在目标类加载到 JVM 时织入,需要特殊的 类加载器,它可以在目标类被引入使用前 增强到该目标类的 字节码
        • AspectJ 可以采用该方式
      • 运行期织入
        • 切面在应用运行的某个时刻被织入
        • 一般情况下,在织入切面时,AOP 容器 会为目标对象 动态 地创建一个代理对象
        • Spring AOP 以该种方式织入

4. 执行流程

  • 多个切面的情况下,可以通过 @Order 指定先后顺序
    • 数字越小,优先级越高

5. 代理

1) 静态代理

  1. 由程序员创建代理类或特定工具自动生成源代码再对其编译
  2. 编译时已经将接口,被代理类(委托类),代理类等都被确定下来,在程序运行前代理类的.class文件已经生成
  3. 缺点:
  • 一个代理类只能实现一个 委托类,虽然可以 implements 多个接口,但违背单一职责原则,所以要代理多个委托类,就需要写多个代理类,浪费时间和精力

代码实现:

2) 动态代理

  1. 在程序运行后通过反射创建生成字节码再由 JVM 加载而成
  2. Spring AOP 中的动态代理主要有两种方式:
    • JDK动态代理
      • 只能代理实现了接口的类
      • 通过与委托类实现同样的接口,然后在实现的接口方法里进行增强来实现的
    • CGLIB动态代理
      • 可以代理未实现任何接口的类
      • 不能代理声明为 final 类型的类和方法,因为它是通过生成一个被代理类的子类来拦截被代理类的方法调用
      • private 方法不能被继承,final 方法不能被重写,static 方法和继承不相干

代码实现 - JDK 动态代理

代码实现 - CGLib 动态代理

  • 底层原理:
    • 通过字节码增强技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
    • 由于反射的效率比较低,所以 CGlib 采用了 FastClass 的机制来实现对被拦截方法的调用
    • FastClass 机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法
    • GCLib 创建的 代理对象 性能高,但是 代理对象的创建过程 比 JDK 要长
      • 对于无需频繁创建的对象,例如 单例对象,采用 GCLib 合适;反之,采用 JDK 更合适

6. Spring AOP VS AspectJ AOP

Spring AOPAspectJ AOP
运行期织入编译期织入、编译后织入、类加载后织入
基于 动态代理 实现静态织入,通过 修改代码 实现
只支持 方法 的织入可增强 构造函数类静态变量字段方法
只支持 Spring IoC 管理的 bean所有 类
性能较慢性能快,代理类无额外运行开销,在运行前已经完成织入工作
javac 编译器需要单独的 编译器 ajc
提供最普通的 AOP提供功能强大的 AOP 完全解决方案
  • 编译期织入
    • .java 进行代码增强
  • 编译后织入
    • 对已经生成 .class.jar 文件进行代码增强
  • 类加载后织入
    • 在加载类时 进行织入
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou