Spring AOP实现原理

182 阅读6分钟

AOP核心概念

核心概念解析

  • Aspect(切面): 封装横切关注点的模块,包含多个 AdvicePointcut,如日志切面、事务切面、权限校验切面。
  • Join Point(连接点):程序执行过程中的一个点(如方法调用、异常抛出),可插入切面逻辑的位置。
  • Advice(通知):在特定连接点执行的动作(如前置、后置、环绕处理),如记录方法执行时间、事务提交或回滚。
  • Pointcut(切点):通过表达式匹配一组连接点,定义哪些连接点会被切面处理。
  • Target Object(目标对象):被代理的原始对象(包含业务逻辑的 Bean)。
  • Proxy(代理):由 Spring 生成的代理对象,包装目标对象以插入切面逻辑。
  • Weaving(织入):将切面代码与目标对象关联的过程(编译时、类加载时或运行时)。

Advice(通知)类型

  • @Before(前置通知):在目标方法执行前触发;适用于参数校验、权限控制。
  • @After(后置通知):在目标方法执行后触发(无论是否抛出异常),适用于资源清理(如关闭文件流)。
  • @AfterReturning(返回后通知):在目标方法 正常返回后 触发,可访问返回值。
  • @AfterThrowing(异常通知):在目标方法 抛出异常后 触发,可捕获特定异常类型。
  • @Around(环绕通知):包裹目标方法,控制其执行流程(类似过滤器),需手动调用 proceed() 执行目标方法。

Pointcut(切点)表达式

表达式说明
execution(* com.example.service.*.*(..))匹配 com.example.service 包下所有类的所有方法
@annotation(com.example.anno.Log)匹配被 @Log 注解标记的方法
within(com.example.service.UserService)匹配 UserService 类中的所有方法
args(java.lang.String)匹配参数类型为 String 的方法

代理机制

  • JDK 动态代理
    • 条件:目标对象实现了至少一个接口。
    • 原理:基于接口生成代理类,调用 InvocationHandler.invoke() 插入切面逻辑。
  • CGLIB 动态代理
    • 条件:目标对象未实现接口(或配置强制使用 CGLIB)。
    • 原理:通过继承目标类生成子类代理,覆盖父类方法。

AOP 与 AspectJ 的关系

维度Spring AOPAspectJ
织入时机运行时动态代理编译时或类加载时(支持更丰富的连接点)
性能略低(运行时生成代理)更高(编译时优化)
功能范围仅支持方法级别的连接点支持字段、构造器、静态代码块等连接点
使用场景轻量级应用,无需复杂切面企业级复杂切面需求(如性能监控、安全检查)

JDK动态代理与CGLIB代理的底层实现

JDK 动态代理

  • 核心原理

    • 基于接口:要求目标对象必须实现至少一个接口。

    • 反射机制:通过 java.lang.reflect.ProxyInvocationHandler 动态生成代理类。

    • 代理对象行为:代理类实现目标接口,并将方法调用转发到 InvocationHandler

  • 实现步骤

    • 定义接口与实现类

      public interface UserService {
          void saveUser(User user);
      }
      
      public class UserServiceImpl implements UserService {
          public void saveUser(User user) { /* 业务逻辑 */ }
      }
      
    • 实现 InvocationHandler

      public class JdkProxyHandler implements InvocationHandler {
          private Object target; // 目标对象
      
          public JdkProxyHandler(Object target) {
              this.target = target;
          }
      
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              // 前置增强
              System.out.println("Before method: " + method.getName());
              // 调用目标方法
              Object result = method.invoke(target, args);
              // 后置增强
              System.out.println("After method: " + method.getName());
              return result;
          }
      }
      
    • 生成代理对象

      UserService target = new UserServiceImpl();
      UserService proxy = (UserService) Proxy.newProxyInstance(
          target.getClass().getClassLoader(),
          target.getClass().getInterfaces(), // 必须为接口数组
          new JdkProxyHandler(target)
      );
      proxy.saveUser(new User()); // 调用代理方法
      
  • 源码关键点

    • Proxy.newProxyInstance():动态生成代理类的字节码,其类名通常为 $Proxy0$Proxy1 等。
    • 代理类结构:代理类继承 Proxy 并实现目标接口,所有方法调用均委托给 InvocationHandler.invoke()

CGLIB 动态代理

  • 核心原理

    • 基于继承:通过生成目标类的子类作为代理类(即使目标类未实现接口)。

    • 字节码操作:使用 ASM 库直接修改字节码,生成新类。

    • 方法拦截:通过 MethodInterceptor 接口实现方法增强。

  • 实现步骤

    • 定义目标类

      public class UserService {
          public void saveUser(User user) { /* 业务逻辑 */ }
      }
      
    • 实现 MethodInterceptor

      public class CglibMethodInterceptor implements MethodInterceptor {
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
              // 前置增强
              System.out.println("Before method: " + method.getName());
              // 调用目标方法(通过 FastClass 机制,避免反射)
              Object result = proxy.invokeSuper(obj, args);
              // 后置增强
              System.out.println("After method: " + method.getName());
              return result;
          }
      }
      
    • 生成代理对象

      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(UserService.class); // 设置父类
      enhancer.setCallback(new CglibMethodInterceptor());
      UserService proxy = (UserService) enhancer.create(); // 生成代理对象
      proxy.saveUser(new User()); // 调用代理方法
      
  • 源码关键点

    • Enhancer 类:负责生成代理类的字节码。
    • FastClass 机制:为代理类和目标类生成索引,直接通过索引调用方法,避免反射(性能优于 JDK 代理)。
    • 代理类结构:代理类继承目标类,重写父类方法,并在方法中调用 MethodInterceptor.intercept()

JDK 代理与 CGLIB 代理对比

维度JDK 动态代理CGLIB 动态代理
代理方式基于接口基于继承
目标类要求必须实现接口可为任意类(非 final)
性能生成快,调用慢(反射)生成慢,调用快(FastClass)
方法覆盖仅代理接口方法代理所有非 final 方法
依赖内置 JDK 支持需引入 CGLIB 库(Spring 已默认包含)
代理类名$Proxy0$Proxy1UserService$$EnhancerByCGLIB$$12345678

Spring AOP 的代理选择策略

  • 默认行为:若目标类实现接口 → 使用 JDK 动态代理;若目标类未实现接口 → 使用 CGLIB 动态代理。
  • 强制使用 CGLIB:通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 配置,强制对所有类使用 CGLIB。
  • 排除 final 方法:CGLIB 无法代理 final 方法,需确保目标方法可被重写。

代理对象的生成流程

核心类与接口

  • ProxyFactory
    • 作用:代理对象的配置工厂,用于设置目标对象、切面(Advisor)、通知(Advice)等。
    • 继承关系ProxyFactoryProxyCreatorSupportAdvisedSupport
  • AopProxy
    • 接口:定义代理对象的生成方法 getProxy()
    • JdkDynamicAopProxy:基于 JDK 动态代理实现。
    • ObjenesisCglibAopProxy:基于 CGLIB 动态代理实现(Spring 优化后的版本)。
  • AdvisedSupport:封装代理配置信息(目标对象、切面列表、代理接口等),供 AopProxy 使用。

代理生成流程

  • 配置阶段(ProxyFactory 初始化):开发者通过 ProxyFactory 设置代理所需的元数据。

    // 1. 创建ProxyFactory并配置
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(new UserServiceImpl());       // 目标对象
    proxyFactory.addAdvice(new LoggingAdvice());         // 添加通知(Advice)
    proxyFactory.setInterfaces(UserService.class);       // 指定代理接口(可选)
    // 2. 生成代理对象
    UserService proxy = (UserService) proxyFactory.getProxy();
    
  • 代理生成阶段(AopProxy 创建代理):选择代理策略(JDK或CGLIB)、创建AopProxy实例、生成代理对象。

通知(Advice)的执行链路

责任链模式的核心思想

  • 定义:将多个处理器(拦截器)按顺序连接成链,每个处理器决定是否将请求传递给下一个处理器。
  • 优点:解耦处理器之间的依赖,支持动态扩展处理逻辑。
  • 在 Spring AOP 中的应用:将多个通知(如 @Around)转换为拦截器(MethodInterceptor),形成链式结构,按顺序执行。

拦截器链的构建

  • 拦截器链的组成:每个 Advisor(切面) 会被适配为一个 MethodInterceptor(方法拦截器)。
  • 链的构建过程:Spring 在创建代理对象时,将所有 Advisor 转换为 MethodInterceptor,并按优先级排序,形成拦截器链。

拦截器链执行流程

               +---------------------+
               | MethodInvocation    |
               | (ReflectiveMethod)  |
               +----------+----------+
                          | proceed()
                          v
+----------------+     +----------------+     +----------------+     +----------------+
| Interceptor 1  | --> | Interceptor 2  | --> | Interceptor 3  | --> | Target Method  |
| (@Before)      |     | (@Around)      |     | (@After)       |     |                |
+----------------+     +----------------+     +----------------+     +----------------+

AspectJ注解驱动的AOP解析过程

切面类的识别与注册

  • @Aspect注解:标记一个类为切面。
  • @Component或@Bean:确保切面类被Spring容器管理。
  • AnnotationAwareAspectJAutoProxyCreator:继承自AbstractAutoProxyCreator的后置处理器,负责初始化后生成Proxy。

切点表达式解析与匹配

  • AspectJExpressionPointcut:封装AspectJ切点表达式,实现Pointcut接口的getClassFilter()getMethodMatcher()
  • 表达式解析:使用AspectJ的PointcutParser将字符串表达式转换为抽象语法树(AST)。
  • 匹配逻辑:在运行时检查目标类和方法是否匹配切点表达式。

通知方法适配为Advice

注解Advice类型适配器类
@BeforeMethodBeforeAdviceAspectJMethodBeforeAdvice
@AfterAfterAdviceAspectJAfterAdvice
@AfterReturningAfterReturningAdviceAspectJAfterReturningAdvice
@AfterThrowingThrowsAdviceAspectJAfterThrowingAdvice
@AroundMethodInterceptorAspectJAroundAdvice

构建Advisor并整合到代理

  • 收集Advisor:在AnnotationAwareAspectJAutoProxyCreator中,查找所有切面类的Advisor。
  • 匹配目标Bean:判断当前Bean是否需要被代理(即是否存在匹配的Advisor)。
  • 生成代理对象:根据配置(JDK或CGLIB)生成代理对象,并将Advisor转换为拦截器链。

动态代理生成与拦截链执行

  • 代理对象调用方法:代理对象拦截目标方法调用。
  • 构建拦截器链:所有匹配的Advisor转换为MethodInterceptor,按优先级排序。
  • 链式执行:通过ReflectiveMethodInvocation递归调用拦截器,直至执行目标方法。