news 2026/6/15 18:13:41

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/6/14 14:11:33

JavaEE进阶-SpringBoot三层架构:餐厅模式解析

目录 核心架构&#xff1a;数据流转的“流水线” 1. Entity (实体类) —— com.entity 2. Controller (控制层) —— com.controller 3. Service (业务层) —— com.service 4. Dao (持久层) —— com.dao 辅助架构&#xff1a;让系统更好用的“后勤部” 5. Config (配置…

作者头像 李华
网站建设 2026/6/15 14:33:02

从Bash脚本到Firebase数据库:解决JSON上传问题

在开发过程中,我们常常会遇到各种技术问题,尤其是涉及到云服务和数据管理时。本文将通过一个实际案例,详细讲解如何解决在Bash脚本中使用Firebase CLI上传JSON文件时遇到的错误。 问题背景 用户moco在使用Bash脚本尝试将一个JSON文件上传到Firebase实时数据库时,遇到了40…

作者头像 李华
网站建设 2026/6/14 18:14:24

sward全面介绍(7) - 如何将confluence数据导入sward

1、安装配置 首先安装配置sward 1.1 安装 下载&#xff0c;点此下载&#xff0c;或直接使用命令在线下载安装包 wget -O tiklab-sward-2.0.7.rpm https://install.tiklab.net/app/install/sward/2.0.7/tiklab-sward-2.0.7.rpm 安装&#xff0c;以centos为例&#xff0c;将Li…

作者头像 李华
网站建设 2026/6/15 13:50:44

Solon AI MCP v3.7.3, v3.6.6 发布

Solon AI & MCP&#xff08;支持 LTS&#xff09; Solon AI & MCP &#xff0c;是 Solon 官方推出的 Java 智能体应用开发框架。旨在为 Java 开发者提供统一的接口抽象层&#xff0c;简化与 OpenAI、DeepSeek、QWen 等主流 AI 模型的集成流程&#xff0c;以及简化 Mcp…

作者头像 李华
网站建设 2026/6/15 12:50:20

OpenHarmony Flutter 分布式任务调度:跨设备负载均衡与资源优化方案

前言在开源鸿蒙&#xff08;OpenHarmony&#xff09;全场景分布式生态中&#xff0c;跨设备任务调度是实现多设备算力协同、资源高效利用的核心技术。传统单设备应用受限于硬件性能&#xff0c;无法高效处理高负载任务&#xff08;如 AI 计算、视频渲染、大数据分析&#xff09…

作者头像 李华
网站建设 2026/6/15 11:43:49

探索Qt下的UI皮肤生成器:多风格与编译那些事儿

Qt下UI皮肤生成器&#xff0c;好几套UI皮肤风格。 Qt5.6.1_MinGW的debug下需要将Pro里的PRECOMPILED_HEADER注释掉&#xff0c;在release下编译无问题&#xff1b; 源码&#xff1a; 使用Qt5.6.1_MinGW&#xff0c;Qt5.7.1_msvc编译通过。在Qt开发的世界里&#xff0c;UI皮肤生…

作者头像 李华