Spring的AOP实现原理

Spring的AOP实现原理

Spring AOP(面向切面编程)的核心实现原理是 ​动态代理(Dynamic Proxy)​。它在运行时为目标对象创建一个代理对象,​所有的调用实际上是通过这个代理对象进行的,代理对象在调用真正目标方法的前后(或环绕)织入(Weave)切面逻辑。
以下是详细的实现原理和过程:
核心机制:动态代理
1.​运行时生成代理对象​:Spring AOP 不会修改原始类的字节码(与 AspectJ 编译时织入不同),而是在运行时为被代理的 Bean 动态生成一个子类(使用 CGLIB)或实现接口的代理类(使用 JDK 动态代理)。
2.​拦截方法调用​:当你通过代理对象调用一个方法时,代理对象不会直接调用目标对象的方法。相反,它会将调用委派给一个拦截器链(AOP Alliance 标准的 MethodInterceptor)​。
3.​执行增强逻辑​:这个拦截器链由一系列增强(Advice)组成,每个增强代表一个切面逻辑(例如,@Before、@After、@Around 等注解定义的方法)。​代理对象负责按正确顺序执行这些增强和目标方法本身。
两种代理方式的具体实现
1.​JDK 动态代理 (基于接口)​​

  • ​触发条件​:当目标对象实现了至少一个接口时,Spring AOP 优先选择这种方式(可以通过配置强制使用 CGLIB)。

  • ​实现过程​:

  • Spring 使用 JDK 内置的 java.lang.reflect.Proxy 类。

  • 它会创建一个实现了目标对象所实现的所有接口的新类(代理类)。

  • 同时创建一个实现了 InvocationHandler 接口的对象。在这个 InvocationHandler 的 invoke 方法中,封装了调用目标方法及执行拦截器链的逻辑。

  • 当客户端代码通过接口调用代理对象的方法时,实际上调用的是 InvocationHandler.invoke 方法。

  • ​优点​:Java 标准库自带,无需额外依赖。

  • ​缺点​:只能代理实现接口的目标类。
    2.​CGLIB 动态代理 (基于子类化)​​

  • ​触发条件​:

  • 目标对象没有实现任何接口。

  • 或者配置显式指定强制使用 CGLIB(即使有接口)。

  • ​实现过程​:

  • Spring 使用第三方库 ​CGLIB。

  • 它会动态生成一个目标类的子类​(代理类)。

  • 覆盖父类(目标类)中的所有非 final 的方法。

  • 在覆盖的方法中,将调用委派给一个或多个 MethodInterceptor(类似于 JDK 的 InvocationHandler)。这个 MethodInterceptor 实现中同样封装了调用目标方法(父类方法)和执行拦截器链的逻辑。

  • ​优点​:可以代理没有实现接口的普通类。

  • ​缺点​:

  • 需要引入额外的库 (cglib 和可能需要的 asm)。(现代 Spring Boot 自动管理这些依赖)

  • 无法代理 final 类或 final/private/static 方法。

  • 创建代理对象的速度通常比 JDK 动态代理稍慢(在现代 JVM 上差别已不大),方法调用的性能与 JDK 代理相当或更好。
    Spring AOP 如何工作(流程)
    1.​定义切面​:开发者使用 @Aspect 注解或 XML 定义切面类,并在其中使用 @Before、@After、@Around、@AfterReturning、@AfterThrowing 等注解声明通知(Advice)和切入点(Pointcut)。
    2.​Bean 初始化​:Spring 容器在启动时,会查找所有被 @Aspect 注解的 Bean 以及所有配置的切面。
    3.​创建代理​:对于每一个被一个或多个切点匹配到的 Spring Bean(称为目标对象):

  • Spring 容器根据上述规则(是否有接口)决定使用 JDK 动态代理还是 CGLIB。

  • 使用相应的技术动态创建一个代理对象。

  • ​这个代理对象取代了原始目标对象被注入到其他依赖它的 Bean 中。​​
    4.​方法调用拦截 & 通知链执行​:

  • 当客户端代码通过代理对象调用一个方法时,代理对象被激活。

  • 代理对象检查该方法是否被任何已定义的切入点所匹配。

  • 如果匹配到了:

  • 创建一个 ​MethodInvocation (或 ProxyMethodInvocation) 对象,它代表当前的方法调用(包含目标对象、方法、参数信息),并持有按顺序排列的通知链(List)​。

  • 触发MethodInvocation.proceed() 方法。

  • 在 proceed() 方法内部:

  • 它维护一个当前通知的索引计数器。

  • 如果还有通知未执行,则取出下一个通知(MethodInterceptor)并调用其 invoke(this) 方法(this 指向 MethodInvocation 本身)。

  • 在环绕通知 (@Around) 中,开发者通常会在其代码中显式调用 proceed() 来驱动调用链继续执行,直到目标方法被调用。

  • 前置通知 (@Before) 在目标方法执行前被调用链执行(通常在 proceed() 调用前)。

  • 后置通知 (@AfterReturning) 在目标方法正常返回后被调用。

  • 异常通知 (@AfterThrowing) 在目标方法抛出异常后被调用。

  • 最终通知 (@After) 无论目标方法如何结束(正常返回或异常)都会被调用。

  • 最终,当所有前置和环绕通知的前半部分都执行后,会调用真正的目标对象的原始方法。

  • 然后,结果(或异常)沿着调用链向上返回,期间执行环绕通知的后半部分、后置通知等。

  • 如果一个方法没有被任何切入点匹配,则代理对象直接调用原始目标对象的相应方法。
    关键组件协同工作

  • org.springframework.aop.framework.ProxyFactory / ProxyFactoryBean​:负责创建 AOP 代理对象的核心工厂类,接受目标对象和通知/切面。

  • org.aopalliance.intercept.MethodInterceptor​:AOP 联盟定义的拦截器接口。Spring 的各种通知(如 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAroundAdvice)以及适配器最终都会实现或适配成 MethodInterceptor 加入到拦截器链中。

  • org.springframework.aop.framework.ReflectiveMethodInvocation​:MethodInvocation 的标准实现类,封装了一次方法调用的所有上下文信息(目标对象、方法、参数、代理对象、通知链),并驱动通知链的执行流程。

  • org.springframework.aop.framework.AdvisedSupport​:持有构建代理所需的所有信息(目标源、通知链、通知器 Advisor 列表等),是 ProxyFactory 的基类。
    重要特点与局限性

  • ​纯 Java 实现​:不依赖特殊的类加载器或编译器,只在运行时生成代理。

  • ​基于代理​:只对 ​Spring IoC 容器管理的 Bean​ 有效。直接 new 出来的对象或未被 Spring 管理的对象不受代理影响。

  • ​仅方法拦截​:Spring AOP 只提供方法执行连接点的拦截(如 @Before(“execution(* com.example.service..(…))”))。它无法拦截字段访问修改、构造器调用、静态初始化块等,这些需要 AspectJ。

  • ​不能代理内部方法调用​:在目标对象内部,一个方法 a() 调用另一个方法 b(),这个对 b() 的调用是在目标对象内部发生的,不会经过代理对象,因此 b() 上的通知不会被执行。需要使用 AopContext.currentProxy() 或重构代码。

  • ​性能​:代理对象创建是一次性的,创建后方法调用的开销很小(主要是几次虚方法调用和条件判断)。对于性能极度敏感的场景,CGLIB 在调用时代理性能通常略好于 JDK(创建时略慢),但在现代 JVM 上差异通常可以忽略。
    ​总结一下流程:​​
    客户端代码 -> 调用被代理Bean的方法 -> 实际调用的是动态生成的代理对象 (JDK Proxy或CGLIB子类) -> 代理对象查找匹配该方法的通知链 -> 创建MethodInvocation包装本次调用 -> 驱动MethodInvocation.proceed() -> 按顺序执行拦截器链中的 MethodInterceptor (对应各种通知) -> 最终在链中调用原始目标对象的方法 -> 结果/异常沿链返回 -> 代理对象将结果返回给客户端。
    因此,Spring AOP的本质是在运行时为目标Bean创建代理,通过动态代理机制拦截匹配的方法调用,并在目标方法执行前后或周围织入开发者定义的切面逻辑,实现了解耦的非功能性需求。

    Spring AOP(面向切面编程)的核心实现原理是动态代理​(Dynamic Proxy)。其核心思想是在运行时为需要增强的目标对象(被代理对象)创建一个代理对象,由代理对象控制对原始方法的调用,并在调用前后插入预定义的横切逻辑(如日志、事务管理等)。以下是详细实现原理:
    核心机制解析
    Spring AOP 通过两种方式实现动态代理,根据目标对象特性自动选择:
    1.​JDK 动态代理(基于接口)​​

  • ​适用场景​:目标对象实现了至少一个接口。

  • ​原理​:

  • 通过 Java 的 java.lang.reflect.Proxy 类生成代理对象。

  • 代理类会实现目标对象的所有接口。

  • 调用代理对象方法时,请求会被转发到 InvocationHandler.invoke() 方法。

  • 在 invoke() 方法中执行拦截器链(包含前置通知、后置通知等)并调用目标方法。

  • ​优势​:JDK 原生支持,无需额外依赖。

  • ​局限性​:只能代理接口方法,无法代理无接口的类。
    2.​CGLIB 动态代理(基于子类继承)​​

  • ​适用场景​:目标对象未实现接口(如普通类)。

  • ​原理​:

  • 通过 ASM 字节码框架动态生成目标类的子类作为代理对象。

  • 重写父类(目标类)的非 final 方法。

  • 在子类方法中加入拦截逻辑,通过 MethodInterceptor 接口的 intercept() 方法执行通知链。

  • ​优势​:可代理普通类,更灵活。

  • ​局限性​:无法代理 final 类或 final/private/static 方法。
    工作流程详解
    1.​切面定义​:

  • 开发者通过 @Aspect 注解定义切面类,使用 @Before、@After、@Around 等注解声明通知(Advice)和切入点(Pointcut)。
    2.​代理对象创建(Spring 容器启动时)​​:

  • Spring 扫描所有被 @Aspect 标记的类及配置的切面。

  • 对每个需代理的 Bean,根据其接口情况选择 JDK 或 CGLIB 动态生成代理对象。

  • ​代理对象替换原始 Bean​ 注入到依赖它的其他 Bean 中。
    3.​方法调用拦截与执行​:

  • 当通过代理对象调用方法时:

  • 代理检查方法是否匹配切入点表达式。

  • 若匹配,创建 MethodInvocation 对象(封装目标方法、参数及拦截器链)。

  • 按顺序执行拦截器链​(前置通知 → 目标方法 → 后置通知 → 返回/异常处理)。

  • @Around 通知可手动控制目标方法执行时机(通过调用 ProceedingJoinPoint.proceed())。
    关键组件协作

  • ProxyFactory​:创建代理对象的核心工厂,接收目标对象和通知列表。

  • MethodInterceptor​:AOP 联盟标准接口,所有通知最终适配为此类型加入拦截器链。

  • AdvisedSupport​:存储代理配置(目标对象、通知链、切点等)。

  • ReflectiveMethodInvocation​:驱动拦截器链执行,维护通知索引链顺序。
    Spring AOP 的典型特性与局限

  • ​优势​:

  • 纯 Java 实现,无需特殊编译器。

  • 与 Spring IoC 容器无缝集成。

  • 对开发者透明,通过配置实现解耦。

  • ​局限性​:

  • ​仅作用于 Spring 管理的 Bean​:直接 new 的对象或非 Spring Bean 无法被代理。

  • ​仅支持方法级拦截​:无法拦截字段访问、构造方法等(需 AspectJ)。

  • ​内部调用问题​:同一类中方法 A 调用方法 B 时,B 的通知不生效(因未通过代理对象)。

  • ​性能​:运行时生成代理会有轻微性能开销,但对大多数场景影响可忽略。
    总结流程
    客户端调用 → 代理对象拦截 → 检查方法是否匹配切点
    ↓ (匹配)
    创建 MethodInvocation → 按顺序执行拦截器链(通知逻辑)

    调用目标方法 → 返回结果/异常 → 执行剩余通知(如后置、异常处理)

    结果返回客户端
    通过动态代理机制,Spring AOP 实现了非侵入式的横切关注点分离,使得业务逻辑与系统级功能(如事务、日志)解耦,大幅提升代码可维护性和复用性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值