news 2026/4/16 14:28:57

Spring AOP 源码深度解析:从代理创建到通知执行的完整链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AOP 源码深度解析:从代理创建到通知执行的完整链路

Spring AOP 源码深度解析:从代理创建到通知执行的完整链路

在上一篇文章中,我们掌握了 Spring AOP 的基本用法和核心概念。但“知其然”之后,更要“知其所以然”。

今天,我们将深入 Spring Framework 源码(以Spring 6.2.x为例),一步步拆解Spring AOP 的实现原理,揭开动态代理与通知链的神秘面纱。


一、入口:@EnableAspectJAutoProxy做了什么?

当你在配置类上加上:

@EnableAspectJAutoProxy public class AopConfig {}

这个注解的定义如下(简化):

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(AspectJAutoProxyRegistrar.class) // ← 关键! public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }

▶ 核心动作:注册一个 BeanDefinition 后处理器

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar,它的作用是在容器启动时向 Spring 容器注册一个特殊的 BeanPostProcessor

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(...) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); } }

继续跟进,最终会注册一个名为internalAutoProxyCreator的 Bean,其实现类是:

AnnotationAwareAspectJAutoProxyCreator

这是整个 Spring AOP 的核心引擎,它继承自:

AnnotationAwareAspectJAutoProxyCreator └── AspectJAwareAdvisorAutoProxyCreator └── AbstractAdvisorAutoProxyCreator └── AbstractAutoProxyCreator └── SmartInstantiationAwareBeanPostProcessor

也就是说,它是一个BeanPostProcessor—— 这意味着它能在每个 Bean 初始化前后进行干预!

二、代理对象何时创建?

AbstractAutoProxyCreator重写了postProcessAfterInitialization方法:

@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); // ← 关键方法 } } return bean; }

wrapIfNecessary:决定是否需要代理

这个方法做了三件事:

  1. 跳过不需要代理的 Bean(如 Advisor、Advice 本身)
  2. 查找所有匹配当前 Bean 的 Advisor(通知器)
  3. 如果有匹配的 Advisor,则创建代理对象
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 1. 获取所有适用的 Advisor(包含 Pointcut + Advice) Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 2. 创建代理 Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return bean; }

关键点:只有存在匹配的切面(Advisor),才会为该 Bean 创建代理!

三、如何查找匹配的 Advisor?—— 切面的“匹配逻辑”

getAdvicesAndAdvisorsForBean()最终会调用:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { List<Advisor> candidateAdvisors = this.findCandidateAdvisors(); List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); this.extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { try { eligibleAdvisors = this.sortAdvisors(eligibleAdvisors); } catch (BeanCreationException ex) { throw new AopConfigException("Advisor sorting failed with unexpected bean creation, probably due to custom use of the Ordered interface. Consider using the @Order annotation instead.", ex); } } return eligibleAdvisors; }

其中,findAdvisorsThatCanApply会遍历每个Advisor,并调用其内部的PointcutgetClassFilter().matches()getMethodMatcher().matches()

例如,你写的:

@Pointcut("execution(public * org.example.spring.aop.service..*.*(..))")

会被解析为一个AspectJExpressionPointcut,其matches()方法会使用AspectJ 表达式引擎判断目标方法是否匹配。

🔍 注意:Spring AOP 虽然使用 AspectJ 的注解和表达式语法,但匹配逻辑由 Spring 自己实现,并未依赖完整的 AspectJ 编译器。

四、代理对象如何创建?—— JDK Proxy vs CGLIB

createProxy()方法内部会根据配置和目标类特性选择代理方式:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 满足任一条件时,优先考虑使用 CGLIB 代理: // - config.setOptimize(true):启用优化(已废弃,但保留兼容) // - config.setProxyTargetClass(true):强制使用 CGLIB(如 @EnableAspectJAutoProxy(proxyTargetClass = true)) // - 没有用户显式指定接口(即目标类未实现任何业务接口) if (config.isOptimize() || config.isProxyTargetClass() || !config.hasUserSuppliedInterfaces()) { Class<?> targetClass = config.getTargetClass(); // 校验:必须能确定目标类型(要么有目标类,要么有接口) if (targetClass == null && config.getProxiedInterfaces().length == 0) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 若目标类为 null、是接口、已是 JDK 代理类、或是 Lambda 表达式生成的类, // 则仍使用 JDK 动态代理(CGLIB 无法代理接口或特殊类) if (targetClass == null || targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 否则使用 CGLIB 代理(通过 Objenesis 优化实例化性能) return new ObjenesisCglibAopProxy(config); } else { // 明确指定了接口且未强制 CGLIB → 使用 JDK 动态代理 return new JdkDynamicAopProxy(config); } }
  • 若目标类实现了接口 → 默认用JDK Proxy
  • 若未实现接口 或 设置了proxyTargetClass=true→ 用CGLIB

✅ Spring 默认优先使用 JDK 动态代理(基于接口),只有在必要时才回退到 CGLIB。

五、方法调用时:通知如何执行?—— 责任链模式

假设我们获取的是一个JDK 代理对象,其InvocationHandlerJdkDynamicAopProxy

当调用orderService.placeOrder(...)时,实际执行的是:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1. 构建拦截器链(Interceptor Chain) List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { // 无通知,直接调用目标方法 return method.invoke(target, args); } else { // 2. 执行责任链 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); return invocation.proceed(); // ← 递归执行通知链 } }

▶ 拦截器链(Interceptor Chain)的组成

每个@Before@AfterReturning等注解,最终都会被包装成一个MethodInterceptor

注解对应的 Interceptor
@BeforeMethodBeforeAdviceInterceptor
@AfterReturningAfterReturningAdviceInterceptor
@Around直接实现MethodInterceptor
@AfterThrowingThrowsAdviceInterceptor

这些 Interceptor 按照通知类型 + 切面优先级排序,形成一条链。

proceed()的递归执行机制

ReflectiveMethodInvocation.proceed()是典型的责任链递归实现:

public Object proceed() throws Throwable { if (currentInterceptorIndex == interceptors.length - 1) { // 所有通知执行完毕,调用目标方法 return method.invoke(target, arguments); } // 获取下一个通知 Object interceptor = interceptors[++currentInterceptorIndex]; if (interceptor instanceof MethodInterceptor) { return ((MethodInterceptor) interceptor).invoke(this); // ← 递归 } // ... }

@Before+@Around+@AfterReturning为例,执行顺序如下:

Around.before() → Before通知 → 目标方法 → AfterReturning通知 → Around.after()

环绕通知(@Around)拥有最高控制权:它可以决定是否调用proceed(),甚至修改参数或返回值。

六、为什么“自调用”不触发 AOP?

考虑以下代码:

@Service public class OrderService { public void methodA() { this.methodB(); // ← 自调用! } @Transactional public void methodB() { ... } }

▶ 根本原因:绕过了代理对象

  • Spring 容器中保存的是代理对象(Proxy)
  • 但在methodA()内部,this指向的是原始的 OrderService 实例,而非代理
  • 因此this.methodB()直接调用原始方法,不会进入JdkDynamicAopProxy.invoke()

▶ 解决方案

  1. 注入自身(推荐)
@Autowired private OrderService self; // 注入的是代理对象 public void methodA() { self.methodB(); // 通过代理调用 }
  1. 使用AopContext.currentProxy()(需开启暴露):
@EnableAspectJAutoProxy(exposeProxy = true) public class AopConfig {} public void methodA() { ((OrderService) AopContext.currentProxy()).methodB(); }

⚠️ 注意:第二种方式耦合了 AOP 框架,一般不推荐。

七、总结 Spring AOP 执行流程

[Spring 容器启动] ↓ @EnableAspectJAutoProxy → 注册 AnnotationAwareAspectJAutoProxyCreator ↓ Bean 初始化完成 → postProcessAfterInitialization() ↓ wrapIfNecessary() → 查找匹配的 Advisor ↓ 存在匹配切面? → 是 → createProxy() → 返回代理对象 ↓ 否 返回原始 Bean ↓ 调用代理方法 → JdkDynamicAopProxy.invoke() / CglibAopProxy.intercept() ↓ 构建 Interceptor 链 → ReflectiveMethodInvocation.proceed() ↓ 依次执行 @Before → @Around → 目标方法 → @AfterReturning/@AfterThrowing → @After

八、结语:AOP 的本质是“代理 + 责任链”

Spring AOP 并非魔法,而是巧妙结合了:

  • BeanPostProcessor:在 Bean 初始化后动态包装
  • 动态代理(JDK/CGLIB):拦截方法调用
  • 责任链模式:组织多个通知的执行顺序
  • AspectJ 表达式:提供灵活的切入点匹配能力

理解了这套机制,你不仅能用好 AOP,还能在排查“为什么 AOP 不生效”时直击根源

📌关注我,每天5分钟,带你从 Java 小白变身编程高手!
👉 点赞 + 关注,让更多小伙伴一起进步!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 16:12:10

Wan2.2-T2V-A14B能否生成适配不同肤色人种的多样化角色

Wan2.2-T2V-A14B能否生成适配不同肤色人种的多样化角色 在影视广告、数字教育和虚拟内容爆发式增长的今天&#xff0c;AI生成视频正在从“能出画面”迈向“懂文化、识身份”的新阶段。过去我们常看到AI生成的人物清一色是白人面孔&#xff0c;深肤色角色要么缺失&#xff0c;要…

作者头像 李华
网站建设 2026/4/16 6:31:55

车联网时序数据库哪个好

车联网时序数据库行业分析&#xff1a;TDengine脱颖而出行业痛点分析在车联网时序数据库领域&#xff0c;当前面临着诸多技术挑战。车联网产生的数据具有海量、高并发、实时性强等特点。随着车辆数量的不断增加以及车辆智能化程度的提升&#xff0c;数据量呈现爆炸式增长。测试…

作者头像 李华
网站建设 2026/4/16 9:17:08

为什么顶尖数据团队都在用R Shiny做多模态展示?真相令人震惊

第一章&#xff1a;为什么顶尖数据团队都在用R Shiny做多模态展示&#xff1f;真相令人震惊在当今数据驱动的决策环境中&#xff0c;静态图表和固定报告已无法满足复杂分析需求。顶尖数据团队正悄然转向 R Shiny&#xff0c;将其作为构建交互式、多模态数据展示的核心工具。Shi…

作者头像 李华
网站建设 2026/4/16 10:58:58

终极指南:深度解析Intel CPU电压调节的完整技术方案

终极指南&#xff1a;深度解析Intel CPU电压调节的完整技术方案 【免费下载链接】Universal-x86-Tuning-Utility Unlock the full potential of your Intel/AMD based device. 项目地址: https://gitcode.com/gh_mirrors/un/Universal-x86-Tuning-Utility 在x86硬件性能…

作者头像 李华
网站建设 2026/4/16 10:58:23

机器学习算法二:逻辑回归

今天继续进行机器学习算法的学习&#xff0c;在上一篇博客中我们介绍了knn算法和线性回归。逻辑回归其实是在线性回归的基础上演变出来的。逻辑回归的作用&#xff1a;实现二分类。原理拆解&#xff1a;本质&#xff1a;为什么说逻辑回归还是线性模型&#xff0c;因为逻辑回归本…

作者头像 李华
网站建设 2026/4/16 12:34:15

JetBrains IDE试用期重置神器:ide-eval-resetter完整使用指南

JetBrains IDE试用期重置神器&#xff1a;ide-eval-resetter完整使用指南 【免费下载链接】ide-eval-resetter 项目地址: https://gitcode.com/gh_mirrors/id/ide-eval-resetter 想要免费体验JetBrains全家桶的强大功能却苦于试用期限制&#xff1f;ide-eval-resetter正…

作者头像 李华