news 2026/3/20 22:59:28

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/3/13 22:26:58

别再把数据管道当“体力活”了:从单体任务到事件驱动的升级之路

别再把数据管道当“体力活”了&#xff1a;从单体任务到事件驱动的升级之路 作者&#xff1a;Echo_Wish兄弟们&#xff0c;咱们今天聊点“掏心窝子”的大数据经验&#xff1a;现代数据管道到底应该怎么设计&#xff1f; 很多公司到现在还在用“单体式任务管道”——Airflow 一堆…

作者头像 李华
网站建设 2026/3/17 9:30:43

【面试现场】谢飞机大战Java面试官:从基础到架构的爆笑面试实录

【面试现场】谢飞机大战Java面试官&#xff1a;从基础到架构的爆笑面试实录 第一轮&#xff1a;Java基础与集合框架 面试官&#xff1a;谢飞机同学&#xff0c;你好。我是今天的面试官&#xff0c;我们开始第一轮技术面试。首先问个基础问题&#xff1a;Java中ArrayList和Linke…

作者头像 李华
网站建设 2026/3/13 3:38:38

2025.12.11总结

工作日精进&#xff1a;明天版本过点&#xff0c;今晚每个人都搞得很晚&#xff0c;我也是晚上11&#xff1a;30才回到租房。每天就这么起床&#xff0c;上班&#xff0c;吃饭&#xff0c;下班。租房&#xff0c;工位&#xff0c;食堂&#xff0c;三点一线。下班后&#xff0c;…

作者头像 李华
网站建设 2026/3/12 17:28:28

C++提高编程学习(更新中....)

1 模板1.1 模板的概念模板是一种通用的编程工具&#xff0c;允许使用虚拟类型&#xff08;泛型&#xff09;定义函数或类&#xff0c;从而实现对不同数据类型的复用。通过模板编写的代码可以自动适应多种数据类型&#xff0c;减少重复代码。模板的特点&#xff1a;通用性&#…

作者头像 李华
网站建设 2026/3/17 11:36:31

编程范式悄然转舵:从“规则编织”到“模型生长”​

我们正站在编程史的拐点——从“基于规则编程”的精密齿轮时代&#xff0c;迈入“基于基础模型编程”的智能涌现纪元。过去&#xff0c;我们用代码刻写每一条逻辑边界&#xff0c;像搭积木般构建功能&#xff1b;如今&#xff0c;大模型成了“会学习的土壤”&#xff0c;高价值…

作者头像 李华