news 2026/4/18 8:15:20

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/17 16:25:47

【企业级Agent安全配置】:Docker环境下99%的人都忽略的5大安全隐患

第一章&#xff1a;企业级Agent安全配置的核心挑战在现代分布式系统架构中&#xff0c;Agent作为连接终端节点与中央管理平台的关键组件&#xff0c;承担着数据采集、指令执行和状态上报等核心职责。然而&#xff0c;随着攻击面的不断扩展&#xff0c;企业级Agent的安全配置面临…

作者头像 李华
网站建设 2026/4/18 1:10:19

Comsol 超构表面远场偏振态绘制那些事儿

Comsol绘制超构表面远场偏振态 动量空间远场偏振far field polarization 绘制教程。 C点 V点识别 Comsol 超构表面动量空间参数图绘制在超构表面的研究领域中&#xff0c;利用 Comsol 绘制远场偏振态以及动量空间相关参数图是非常重要的工作。今天就来跟大家唠唠这其中的门道…

作者头像 李华
网站建设 2026/4/17 7:45:53

1.19 UGUI的准备数据流程

1.UGUI准备数据的流程a.PostLateUpdate.PlayerUpdateCanvases- 作用: 这是Unity在每帧的晚期更新(LateUpdate)之后, 专门用于更新所有Canvas(UI画布)的系统函数; 它负责驱动整个UI渲染流程- 详细流程: 它会调用Canvas.SendWillRenderCanvases(), 从而触发一系列UI更新操作, 包括…

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

MCP SC-400量子加密实战指南(从零到企业级安全架构)

第一章&#xff1a;MCP SC-400量子安全配置实务概述在当前量子计算快速发展的背景下&#xff0c;传统加密体系面临前所未有的破解风险。MCP SC-400作为新一代量子安全通信协议标准&#xff0c;旨在提供抗量子攻击的安全配置框架&#xff0c;保障关键基础设施与敏感数据的长期安…

作者头像 李华
网站建设 2026/4/18 3:58:34

JavaEE进阶——MyBatis动态SQL与图书管理系统实战

目录 MyBatis 进阶详解与图书管理系统实战 第一部分&#xff1a;核心知识点深度解析 1. 什么是动态 SQL&#xff1f;为什么需要它&#xff1f; 2. 动态 SQL 标签详解&#xff08;文档核心点扩展&#xff09; 2.1 标签&#xff1a;最常用的判断逻辑2.2 标签&#xff1a;万能…

作者头像 李华
网站建设 2026/4/18 3:57:51

初始网络原理

理论知识网络的发展历程单机时代->局域网->广域网->移动互联网时代(国内的网络发展相对较慢,2000年前后,才真正的进入了网络时代)组建网络的核心设备路由器和交换机是组建网络的核心设备(交换机可以认为是对路由器的接口进行拓展)网络通信基础知识网络互联的目的是为了…

作者头像 李华