Spring AOP实现原理

简介: 本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。

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

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$Proxy1 UserService$$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类型 适配器类
@Before MethodBeforeAdvice AspectJMethodBeforeAdvice
@After AfterAdvice AspectJAfterAdvice
@AfterReturning AfterReturningAdvice AspectJAfterReturningAdvice
@AfterThrowing ThrowsAdvice AspectJAfterThrowingAdvice
@Around MethodInterceptor AspectJAroundAdvice

构建Advisor并整合到代理

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

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

  • 代理对象调用方法:代理对象拦截目标方法调用。
  • 构建拦截器链:所有匹配的Advisor转换为MethodInterceptor,按优先级排序。
  • 链式执行:通过ReflectiveMethodInvocation递归调用拦截器,直至执行目标方法。
相关文章
|
2月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
411 0
|
1月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
3月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
3月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
10月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
527 6
|
9月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
666 25
|
9月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
437 24
|
8月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
1029 0
|
8月前
|
Java 开发者 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——什么是AOP
本文介绍了Spring Boot中的切面AOP处理。AOP(Aspect Oriented Programming)即面向切面编程,其核心思想是分离关注点。通过AOP,程序可以将与业务逻辑无关的代码(如日志记录、事务管理等)从主要逻辑中抽离,交由专门的“仆人”处理,从而让开发者专注于核心任务。这种机制实现了模块间的灵活组合,使程序结构更加可配置、可扩展。文中以生活化比喻生动阐释了AOP的工作原理及其优势。
510 0

热门文章

最新文章