Spring 源码系列(12): @EnableAspectJAutoProxy 到底注册了什么

📌 阅读前提示 :上篇我们把 AOP 的术语和注解讲透了。从本篇起连续三篇钻源码。本篇回答一个最基础的问题:@EnableAspectJAutoProxy 这个注解,在容器启动时到底「偷偷」干了什么?答案只有一句话------往容器里注册了一个 BeanPostProcessor。但这句话背后的调用链,值得你跟一遍。

一、引子:一个被低估的注解

很多人的认知停留在「加了 @EnableAspectJAutoProxy 注解,AOP 就生效了」。但面试一旦被追问「它注册了哪个类?这个类在哪一步介入 Bean 生命周期?」,就卡壳了。

本篇带你把这条链走通:

复制代码
@EnableAspectJAutoProxy
   → @Import(AspectJAutoProxyRegistrar.class)
      → registerBeanDefinitions()
         → 注册 internalAutoProxyCreator
            = AnnotationAwareAspectJAutoProxyCreator (一个 BeanPostProcessor)

记住结论:@EnableAspectJAutoProxy 本身不创建代理,它只是把一个「能创建代理的 BPP」塞进容器。


二、源码追踪:从注解到注册

2.1 注解的 @Import

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)   // ← 关键:导入一个 Registrar
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;  // 是否强制 CGLIB
    boolean exposeProxy() default false;       // 是否暴露代理到 AopContext
}

@Import 在 IoC 阶段(第 5 篇第 5 步 invokeBeanFactoryPostProcessors)被 ConfigurationClassPostProcessor 解析,触发 AspectJAutoProxyRegistrar.registerBeanDefinitions()

2.2 AspectJAutoProxyRegistrar 注册核心类

java 复制代码
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                         BeanDefinitionRegistry registry) {
        // ① 注册自动代理创建器(如果容器中还没有)
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        // ② 读取 @EnableAspectJAutoProxy 的两个属性,回写到 BD
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); // 强制 CGLIB
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);      // 暴露代理
            }
        }
    }
}

2.3 注册的真实类名

java 复制代码
// AopConfigUtils.java
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {

    return registerOrEscalateApcAsRequired(
            AnnotationAwareAspectJAutoProxyCreator.class, registry, source); // ← 真正注册的类
}

最终以 Bean 名 org.springframework.aop.config.internalAutoProxyCreator 注册了一个 AnnotationAwareAspectJAutoProxyCreatorBeanDefinition

📌 结论internalAutoProxyCreator 就是 AnnotationAwareAspectJAutoProxyCreator,它实现了 SmartInstantiationAwareBeanPostProcessor(继承链最终是 BeanPostProcessor)。


三、这个 BPP 在生命周期的哪一步介入

回顾第 7 篇生命周期:普通 BeanPostProcessor.postProcessAfterInitializationinitializeBean 末尾执行。AnnotationAwareAspectJAutoProxyCreator 正是利用这一步「动手脚」。

阶段 方法 作用
实例化后、属性填充前 getEarlyBeanReference 处理循环依赖时的早期代理(见第 9 篇)
初始化后 postProcessAfterInitialization 创建 AOP 代理(核心,第 13 篇详述)
java 复制代码
// AbstractAutoProxyCreator.java(父类)
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey); // ← 必要时包成代理
        }
    }
    return bean;
}

💡 重点wrapIfNecessary 是「是否代理」的总开关。如果你的 Bean 不匹配任何切面 Pointcut,这里原样返回,不做任何包装------这也是为什么不是所有 Bean 都被代理。


四、proxyTargetClass / exposeProxy 最终落到哪

这两个属性并非直接存着,而是被 AopConfigUtils 写进 internalAutoProxyCreator 这个 BD 的 propertyValues

java 复制代码
// forceAutoProxyCreatorToUseClassProxying
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
    BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
    definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); // 强制 CGLIB
}
// forceAutoProxyCreatorToExposeProxy
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);          // 暴露代理

后续该 BPP 实例化后,proxyTargetClass / exposeProxy 作为它的字段,在第 14 篇「JDK 还是 CGLIB」的选择逻辑中被读取。


五、Spring Boot 为什么「不用写」这个注解

Spring Boot 的 AopAutoConfiguration 帮你自动 @Import 了等效配置:

java 复制代码
@Configuration
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
@EnableAspectJAutoProxy(proxyTargetClass = true)   // ← Boot 默认 proxyTargetClass=true
public class AopAutoConfiguration {
    // 注意:Boot 默认 exposeProxy=false,需要自调用代理时仍需手动开
}

⚠️ 注意spring.aop.proxy-target-class 默认 true,即 Boot 项目默认走 CGLIB 子类代理(即使你的类实现了接口)。这与「纯 Spring 默认优先 JDK 动态代理」不同,是面试常挖的坑。


六、常见误区

误区 正解
@EnableAspectJAutoProxy 直接创建代理 不,它只注册 AnnotationAwareAspectJAutoProxyCreator 这个 BPP
Boot 项目不用配就走 JDK 动态代理 Boot 默认 proxyTargetClass=true,默认 CGLIB
注册的是 @Aspect 类本身 注册的是「自动代理创建器」,与你的切面类无关
exposeProxy 默认开启 默认 false,自调用代理失效需手动开

🧪 面试题自测

  1. @EnableAspectJAutoProxy 通过什么机制注册类?注册的是哪个类?Bean 名是什么?
  2. AnnotationAwareAspectJAutoProxyCreator 在 Bean 生命周期的哪一步创建代理?
  3. proxyTargetClass=trueexposeProxy=true 最终被写到哪里?
  4. Spring Boot 下 AOP 默认用 JDK 还是 CGLIB?为什么?
  5. 如果容器里已存在 internalAutoProxyCreator,再 @Import 会怎样?(提示:registerOrEscalateApcAsRequired 的优先级升级逻辑)

🔧 Debug 小技巧

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary 打断点,观察 beanName = "org.springframework.aop.config.internalAutoProxyCreator"BeanDefinition 是如何生成的;再在 AbstractAutoProxyCreator.postProcessAfterInitialization 断点,看它如何对所有 Bean「过一遍」wrapIfNecessary


下一篇预告

第 13 篇:代理到底在「什么时机」创建?深入 wrapIfNecessary → getAdvicesAndAdvisorsForBean → createProxy,看清 shouldSkip 排除逻辑,以及为何代理创建发生在初始化之后而非之前。


如果这篇对你有帮助,欢迎 点赞 · 收藏 · 关注 三连支持。

Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。

相关推荐
神经蛙199615 小时前
🌍 别再硬编码中文了!Python Web 项目国际化(i18n)完全指南
后端·python
二月龙15 小时前
Spring 事务失效的 8 种场景,很多老手依然频繁踩雷
后端
掘金酱16 小时前
「TRAE Work 实战帮」征文启动!你沉淀的经验,值得被看见!
前端·人工智能·后端
长大198816 小时前
MyBatis 常见性能陷阱:N+1 查询、一级缓存踩坑解决方案
后端
用户18615580086016 小时前
MinIO Java 对接试用:从连接、上传到下载的完整示例
后端
爱勇宝16 小时前
DeepSeek V4-Flash 更新:代码与 Agent 能力全面增强
前端·后端·deepseek
极客悟道16 小时前
SDKMAN vs jEnv vs JetTUI,JDK 版本管理到底选哪个
后端
长大198816 小时前
Java8 新特性到底要不要吃透?工作中高频使用的 5 个功能总结
后端
风中芦苇啊16 小时前
Java EasyExcel 导入通用工具类:自定义注解映射字段 + 反射机制
java·开发语言