- 文档
- 教程
- 知识库
【免费下载链接】source-code-hunter
😱 从源码层面,剖析挖掘互联网行业主流技术的底层实现原理,为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶,Mybatis、Netty、Dubbo 框架,及 Redis、Tomcat 中间件等
在 Spring 框架中,只需要在 XML 配置文件里写上一行<aop:aspectj-autoproxy/>,基于注解的 Spring AOP 能力就会被激活。这背后隐藏着一条清晰的源码链路:XML 命名空间标签 → 标签解析器(BeanDefinitionParser)→ 自动代理创建器(AutoProxyCreator)的注册与属性装配。本文以 source-code-hunter 仓库中 Spring-Aop如何生效.md 为骨架,完整还原这条链路中的每一个关键类与方法,并结合仓库中 AOP源码实现及分析.md、JDK动态代理的实现原理解析.md 等姊妹文档,讲清楚“一行配置如何驱动整个 AOP 代理机制”。读完本文,你将掌握:如何从任意 XML 标签反查 Spring 源码解析入口、AspectJAutoProxyBeanDefinitionParser的完整解析流程、proxy-target-class与expose-proxy两个属性的底层去向,以及自动代理创建器注册背后的“注册或升级”策略。
一、起点:一行 XML 配置开启 Spring AOP
在使用 Spring AOP 时,XML 配置文件中会出现下面这段代码,用于开启 Spring 对 AspectJ 注解(@Aspect、@Before、@After等)的支持:
<aop:aspectj-autoproxy/>这行配置位于<aop>命名空间之下,等价于在 Java 配置类上使用@EnableAspectJAutoProxy注解。二者的终点殊途同归——最终都会注册一个名为AnnotationAwareAspectJAutoProxyCreator的自动代理创建器 Bean,这一点在仓库的 Spring-Import.md 中也能看到注解路径的佐证:AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar,通过AopConfigUtils完成自动代理创建器的注册。
但本文的主线是 XML 路径。问题来了:Spring 容器是如何解析<aop:aspectj-autoproxy/>这个自定义标签的?解析入口在哪里?
二、如何从 XML 标签反查源码解析入口
Spring 的自定义 XML 标签解析遵循一套固定机制:每个命名空间(namespace)对应一个NamespaceHandler,命名空间内的每个标签对应一个BeanDefinitionParser。<aop:aspectj-autoproxy/>所属的<aop>命名空间,其处理器是org.springframework.aop.config.AopNamespaceHandler。
在源码阅读过程中,定位某个标签解析方法的最高效手段就是直接搜索标签名。如上图所示,在 IDE 中对aspectj-autoproxy做全局搜索(搜索范围为*.java),可以立刻命中关键代码:
// AopNamespaceHandler 的初始化方法中 registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());也就是说,aspectj-autoproxy标签被注册给了AspectJAutoProxyBeanDefinitionParser这个解析器。搜索命中结果同时还出现在EnableAspectJAutoProxy.java、测试资源aspectj-autoproxy-config.xml等位置,印证了该标签在 XML 与注解两条路径中都处于核心位置。
三、AspectJAutoProxyBeanDefinitionParser:标签解析器本体
org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser实现了org.springframework.beans.factory.xml.BeanDefinitionParser接口(见上文类图),因此它的解析入口是parse方法:
@Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 注册 <aop:aspectj-autoproxy/> AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 子类解析 extendBeanDefinition(element, parserContext); return null; }parse方法做了两件事:
- 调用
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(...),这是核心动作——把AnnotationAwareAspectJAutoProxyCreator注册进容器; - 调用
extendBeanDefinition(...)交给子类做扩展解析(默认实现为空操作,供子类覆盖)。
注意parse返回null:该标签并不直接产出一个 BeanDefinition 作为解析结果,它的作用是“副作用式”地向容器注册自动代理创建器,这也解释了为什么标签解析器与普通 bean 的解析在返回语义上存在差异。
四、AopNamespaceUtils:注册自动代理创建器
parse方法调用的核心逻辑位于org.springframework.aop.config.AopNamespaceUtils:
/** * 注册 <aop:aspectj-autoproxy/> * @param parserContext * @param sourceElement */ public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { // 注册或者升级bean BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // proxy-target-class 和 expose-proxy 标签处理 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // 注册组件并且交给监听器 registerComponentIfNecessary(beanDefinition, parserContext); }该方法包含三个步骤:
- 注册或升级 Bean:委托给
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(...),真正的注册逻辑在这里; - 标签属性处理:解析
<aop:aspectj-autoproxy>元素上的proxy-target-class与expose-proxy两个属性(详见本文第六节); - 组件登记:
registerComponentIfNecessary(...)将新注册的 BeanDefinition 登记为组件,并交给监听器(即ReaderEventListener)统一记录,这与 XML 解析过程中“组件注册 + 事件发布”的惯例保持一致。
五、AopConfigUtils:注册或升级AnnotationAwareAspectJAutoProxyCreator
真正的注册逻辑在org.springframework.aop.config.AopConfigUtils中:
@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { // 注册或者升级 AspectJ return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }它把AnnotationAwareAspectJAutoProxyCreator.class作为目标类,交给registerOrEscalateApcAsRequired处理。这个私有方法是整条链路中最关键的一段逻辑:
/** * 注册或者升级 bean * @param cls 类 * @param registry 注册器 * @param source 源类 * @return */ @Nullable private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 判断注册器是否包含org.springframework.aop.config.internalAutoProxyCreator if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 获取注册器 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 创建新的bean对象 if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } // 即将创建的Bean对象和当前的注册器相同返回null return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); // 设置加载顺序 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册bean定义 registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }这段代码包含了几个值得深挖的细节:
1. 固定 Bean 名称。自动代理创建器注册时使用的 Bean 名称是常量AUTO_PROXY_CREATOR_BEAN_NAME,即org.springframework.aop.config.internalAutoProxyCreator。Spring 通过这个固定名称实现“容器内只允许存在一个自动代理创建器”的约束——无论是 XML 路径还是@EnableAspectJAutoProxy注解路径,注册的都是同一个名称下的 Bean。
2. 注册或升级(escalate)策略。如果容器中已经存在名为internalAutoProxyCreator的 BeanDefinition,且其类与本次要注册的类不同,则通过findPriorityForClass比较两者的优先级:只有当新类优先级更高时,才会用apcDefinition.setBeanClassName(cls.getName())把原有 BeanDefinition 的类替换为新类。这就是“注册或升级”的含义——多个自动代理创建器竞争同一个名称时,优先级高的胜出;如果类相同或新类优先级不足,则直接返回null放弃注册。从源码结构可以推断,findPriorityForClass对不同自动代理创建器实现类(如InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator)维护了一套优先级映射。
3. 基础设施角色的两个关键属性。
order = Ordered.HIGHEST_PRECEDENCE:把该 Bean 的order属性设为最高优先级,保证它在所有Ordered组件中被最先处理;role = BeanDefinition.ROLE_INFRASTRUCTURE:标记这是一个基础设施 Bean(infrastructure bean),不属于用户业务 Bean 的范畴,在诸如自动扫描排除、@Configuration类后置处理等场景中会被特殊对待,用户也很难直接感知到它的存在。
4. 注册源信息。beanDefinition.setSource(source)会把 XML 元素的源信息(parserContext.extractSource(sourceElement))挂到 BeanDefinition 上,便于后续诊断报错时回溯配置来源。
至此,AnnotationAwareAspectJAutoProxyCreator已经作为一个基础设施 Bean 进入了 IoC 容器——这正是<aop:aspectj-autoproxy/>一行配置“生效”的实质。
六、proxy-target-class与expose-proxy属性处理
<aop:aspectj-autoproxy>标签还支持两个属性:proxy-target-class与expose-proxy。它们在AopNamespaceUtils.useClassProxyingIfNecessary中被解析:
/** * proxy-target-class 和 expose-proxy 标签处理 */ private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) { if (sourceElement != null) { // 处理 proxy-target-class boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); if (proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 处理 expose-proxy boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); if (exposeProxy) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }处理逻辑很直白:从 XML 元素上读取两个属性(均为布尔值,默认false),只有显式声明为true时才触发对应的强制设置。
proxy-target-class的底层去向由AopConfigUtils.forceAutoProxyCreatorToUseClassProxying实现:
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); } }它把proxyTargetClass=true作为属性写入自动代理创建器的 BeanDefinition。这个属性的消费点位于代理对象生成阶段:仓库的 AOP源码实现及分析.md 中DefaultAopProxyFactory.createAopProxy(...)展示了选择逻辑——当config.isProxyTargetClass()为真、或目标类没有用户提供的代理接口时,若目标类是接口则使用JdkDynamicAopProxy(JDK 动态代理),否则使用CglibAopProxy(CGLIB 生成子类代理)。换句话说,proxy-target-class="true"的作用是强制走 CGLIB 类代理路线,即使目标对象实现了接口也不例外。
expose-proxy的底层去向由forceAutoProxyCreatorToExposeProxy实现,其操作与forceAutoProxyCreatorToUseClassProxying完全同构:同样是将读取到的布尔值放入 bean 对象作为一个属性存储,区别仅在于属性名是exposeProxy。该属性在运行时会被AopContext.setCurrentProxy(proxy)/AopContext.getCurrentProxy()消费(见 AOP源码实现及分析.md 中JdkDynamicAopProxy.invoke()与CglibAopProxy.intercept()对exposeProxy的判断)。它的典型使用场景是:当目标方法内部调用自身方法(this.xxx())时,通过AopContext.currentProxy()拿到代理对象,从而让内部调用也能被切面拦截。
完整的配置示例:
<!-- 开启 AspectJ 注解式 AOP,并强制使用 CGLIB 类代理、暴露代理对象到 AopContext --> <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>七、注册之后的生效链路:从自动代理创建器到代理对象
AnnotationAwareAspectJAutoProxyCreator注册进容器后,AOP 的“生效”才刚刚开始。从 Spring AOP 的整体设计看,该创建器是一个BeanPostProcessor类型的自动代理创建器:容器在实例化每个 Bean 后,会经过它的后处理,凡是命中切点(Pointcut)的 Bean,都会被替换为代理对象。
而代理对象本身的生成与拦截调用,正是仓库 AOP源码实现及分析.md 所讲解的内容,与本文形成完整的阅读闭环:
- 接口的设计:
Advice(通知,定义增强行为)、Pointcut(切点,定义被增强方法的集合)、Advisor(通知器,将 Pointcut 与 Advice 结合,如DefaultPointcutAdvisor); - 代理对象的生成:通过
ProxyFactoryBean(或自动代理创建器内部持有的ProxyFactory)构造AopProxy。核心决策在DefaultAopProxyFactory.createAopProxy(...):目标类是接口则走JdkDynamicAopProxy(JDK 动态代理),否则走CglibAopProxy(CGLIB); - 拦截器链的调用:JDK 代理通过
InvocationHandler.invoke()回调、CGLIB 通过MethodInterceptor.intercept()回调进入ReflectiveMethodInvocation.proceed(),沿interceptorsAndDynamicMethodMatchers链逐个执行拦截器,最后由AopUtils.invokeJoinpointUsingReflection(...)反射调用目标方法; - Advice 的适配:
DefaultAdvisorAdapterRegistry通过MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter等适配器,把不同类型的Advice适配为对应的MethodInterceptor(如MethodBeforeAdviceInterceptor先回调before()再proceed())。
关于 JDK 动态代理本身的字节码级原理——生成的$Proxy0类如何继承Proxy、实现业务接口、并在每个方法中通过super.h.invoke(...)回调InvocationHandler——可进一步阅读仓库的 JDK动态代理的实现原理解析.md,其中给出了用ProxyGenerator.generateProxyClass导出并反编译代理类的完整实验代码。
八、总结
回到最初的问题:<aop:aspectj-autoproxy/>到底是如何让 Spring AOP 生效的?完整的调用链可以归纳为:
<aop:aspectj-autoproxy/>由AopNamespaceHandler分发到AspectJAutoProxyBeanDefinitionParser;parse()调用AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(...);- 该工具方法调用
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(...),最终经registerOrEscalateApcAsRequired(...)以固定名称org.springframework.aop.config.internalAutoProxyCreator注册AnnotationAwareAspectJAutoProxyCreator,并设置order=HIGHEST_PRECEDENCE、role=ROLE_INFRASTRUCTURE; useClassProxyingIfNecessary(...)将proxy-target-class、expose-proxy属性写入该 BeanDefinition;- 该基础设施 Bean 在容器运行期作为自动代理创建器介入 Bean 实例化过程,通过 JDK 动态代理或 CGLIB 为目标 Bean 生成代理并织入拦截器链。
从方法论层面看,本文也印证了一条可复用的 Spring 源码阅读规律:实现org.springframework.beans.factory.xml.BeanDefinitionParser接口的类,多用于对 XML 标签的解析,并且入口为parse方法;如果解析结果是一个 bean 对象,通常会和 Spring 监听器(如ReaderEventListener)一起出现。掌握了“从标签名反查NamespaceHandler注册代码 → 跟踪BeanDefinitionParser→ 追踪工具类注册逻辑”这条路径,面对任何 Spring 自定义 XML 标签都能快速定位其解析实现。
延伸阅读
本文内容整理自仓库中的源码阅读笔记,建议按以下顺序联动阅读:
- Spring-Aop如何生效.md:本文的原始笔记,聚焦标签解析链路;
- AOP源码实现及分析.md:AOP 代理生成与拦截器链调用的完整源码分析;
- JDK动态代理的实现原理解析.md:JDK 动态代理字节码层面的实现剖析;
- Spring-Import.md:注解路径
@EnableAspectJAutoProxy背后的ImportBeanDefinitionRegistrar机制; - BeanFactoryPostProcessor.md:理解容器刷新阶段 Bean 定义的后置处理时机。
- 文档
- 教程
- 知识库
【免费下载链接】source-code-hunter
😱 从源码层面,剖析挖掘互联网行业主流技术的底层实现原理,为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶,Mybatis、Netty、Dubbo 框架,及 Redis、Tomcat 中间件等
相关推荐
Spring AOP 如何生效:从 `<aop:aspectj-autoproxy/>` 标签解析到自动代理的源码级剖析
Spring AOP 如何生效:从 <aop:aspectj autoproxy/ 标签解析到自动代理的源码级剖析 Spring AOP(面向切面编程)是 Sp
文档教程技术博客知识库Hamcrest-php生成器机制解析:了解@factory标签如何自动生成代码
Hamcrest php生成器机制解析:了解@factory标签如何自动生成代码 Hamcrest php是一个强大的PHP测试匹配器库,它通过独特的 @fac
测试GitHub_Trending/sp/spring-reading源码分析:Spring AOP代理选择机制
GitHub_Trending/sp/spring reading源码分析:Spring AOP代理选择机制 1. 代理选择机制核心组件 Spring AOP的
示例工程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考