Spring AOP 代理创建时机深度解析:初始化阶段 vs 三级缓存(源码级)

前言

在 Spring 框架的核心机制中,AOP 代理的创建逻辑是理解 Spring 事务管理、切面编程、循环依赖解决的关键纽带。很多开发者存在两大核心认知误区:一是认为"Spring 会对所有 Bean 统一进行 AOP 代理",二是混淆"初始化阶段创建代理"与"三级缓存提前创建代理"的逻辑边界。

本文将结合 Spring 核心源码 ,从 Bean 完整生命周期 出发,深度拆解 初始化阶段统一创建 AOP 代理 的底层逻辑,明确区分 正常 Bean 与循环依赖 Bean 的 AOP 创建时机,解答"是否所有 Bean 都会被 AOP"这一核心问题,帮你彻底打通 Spring AOP 与三级缓存的关联逻辑。

一、初始化阶段创建 AOP 代理:核心定位与源码

1. 核心定位

非循环依赖 的正常场景下,Spring 并不会通过三级缓存的 ObjectFactory 提前创建代理对象。所有 AOP 代理的创建工作,均会集中在 Bean 生命周期的 initializeBean() 方法中完成 。这是 Spring AOP 的 标准主流程,也是 90% 以上 Bean 的 AOP 代理创建方式。

2. 核心源码:initializeBean 方法

AOP 代理创建的核心入口位于 initializeBean 方法,这是 Bean 初始化的最后一步,源码如下(来自 AbstractAutowireCapableBeanFactory):

java 复制代码
/**
 * 初始化 Bean 的核心方法,包含 Aware 回调、前置处理、初始化方法、后置处理
 * @param beanName Bean 名称
 * @param bean 原始 Bean 实例
 * @param mbd Bean 定义信息
 * @return 初始化后的 Bean 实例(可能是代理对象)
 */
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 1. 执行 Aware 接口回调(BeanNameAware、BeanFactoryAware 等)
    invokeAwareMethods(beanName, bean);

    // 2. 执行 BeanPostProcessor 的前置处理(@PostConstruct 等注解处理也在此阶段)
    Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

    // 3. 执行初始化方法(afterPropertiesSet 自定义初始化方法、init-method 配置)
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Invocation of init method failed", ex);
    }

    // ==========================================
    // 🔥 🔥 🔥 【核心关键】AOP 代理创建在这里完成
    // ==========================================
    // 4. 执行 BeanPostProcessor 的后置处理(AOP 代理创建的核心入口)
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    return wrappedBean;
}

3. 关键核心:applyBeanPostProcessorsAfterInitialization

AOP 代理的创建,本质是通过 Bean 后置处理器(BeanPostProcessor) 实现的。Spring 中负责 AOP 代理创建的核心处理器是 AnnotationAwareAspectJAutoProxyCreator,它实现了 SmartInstantiationAwareBeanPostProcessor 接口,会在 initializeBean 方法的最后一步被触发。

java 复制代码
/**
 * 遍历所有 BeanPostProcessor,执行后置处理
 * @param existingBean 已初始化完成的 Bean 实例
 * @param beanName Bean 名称
 * @return 处理后的 Bean 实例(可能是代理对象)
 */
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) {
    Object result = existingBean;
    // 遍历容器中所有的 BeanPostProcessor
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 执行后置处理方法
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

当遍历到 AnnotationAwareAspectJAutoProxyCreator 时,会进入其 postProcessAfterInitialization 方法,最终触发 AOP 代理的创建逻辑。

二、初始化阶段创建 AOP 代理:具体执行步骤

AnnotationAwareAspectJAutoProxyCreator 作为 AOP 代理创建的核心处理器,在初始化阶段的执行流程分为 4 个关键步骤 ,且并非所有 Bean 都会被代理

步骤 1:匹配切点与拦截器

首先,处理器会判断当前 Bean 是否需要被 AOP 增强,核心逻辑是 匹配切点(Pointcut)

java 复制代码
/**
 * 判断 Bean 是否需要被代理,核心方法
 * @param beanClass Bean 的类对象
 * @param beanName Bean 名称
 * @return 匹配的拦截器数组,无则返回 DO_NOT_PROXY
 */
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 1. 寻找所有匹配当前 Bean 的切面通知器(Advisor)
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        // 无匹配的切面,无需代理,返回 DO_NOT_PROXY
        return DO_NOT_PROXY;
    }
    // 有匹配的切面,封装拦截器返回
    return advisors.toArray();
}

核心判断逻辑

  • 遍历所有自定义的 @Aspect 切面,匹配当前 Bean 的类和方法是否符合切点规则;
  • 检查是否存在 @Transactional 等事务注解,Spring 会自动为其创建代理;
  • 排除系统内部类、基础设施类(如 StringInteger 等)。

步骤 2:创建代理对象

如果当前 Bean 匹配切点规则,处理器会创建 CGLIB 代理JDK 动态代理

java 复制代码
/**
 * 创建 AOP 代理对象
 * @param beanClass 目标类
 * @param beanName Bean 名称
 * @param specificInterceptors 匹配的拦截器
 * @param targetSource 目标源
 * @return 代理对象
 */
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    // 1. 创建代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    // 2. 判断使用 JDK 动态代理还是 CGLIB 代理
    // 目标类实现接口 → 优先使用 JDK 动态代理
    // 目标类未实现接口 → 强制使用 CGLIB 代理
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        } else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 3. 封装拦截器
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);

    // 4. 自定义代理工厂(可扩展)
    customizeProxyFactory(proxyFactory);

    // 5. 创建代理对象
    return proxyFactory.getProxy(getProxyClassLoader());
}

步骤 3:记录代理信息,避免重复创建

为了避免后续重复创建代理,处理器会将代理信息存入缓存:

java 复制代码
// 存入 earlyProxyReferences 缓存,标记已创建早期代理
this.earlyProxyReferences.put(cacheKey, bean);
// 存入 advisedBeans 缓存,标记是否需要代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 存入 proxyTypes 缓存,记录代理类类型
this.proxyTypes.put(cacheKey, proxy.getClass());

步骤 4:返回代理对象

最终,initializeBean 方法会将 代理对象 作为返回值,Spring 容器中管理的不再是原始 Bean 实例,而是代理实例。

三、核心问题:是不是所有 Bean 都会进行 AOP?

绝对不是! 这是 Spring AOP 最核心的认知误区。

Spring AOP 采用 "按需代理" 原则,只有满足以下条件之一的 Bean 才会被创建代理

  1. 类或方法上存在 AOP 切面注解 :如 @Around@Before@After 等切面方法匹配的类/方法;
  2. 存在事务注解@Transactional 标注的类或方法,Spring 会为其创建事务代理;
  3. 实现了 AOP 相关接口 :如 IntroductionInterceptor 等,需要动态代理增强;
  4. 自定义 AOP 规则匹配:通过 XML 配置的 AOP 切点,匹配的 Bean 才会被代理。

不会被 AOP 代理的 Bean 类型

以下 90% 以上的普通 Bean,直接返回原始对象,不会创建代理:

  • @Service@Repository@Component 注解的类,且无事务、无切面匹配;
  • 基础数据类型、包装类、字符串等系统类;
  • 仅实现普通业务逻辑,无任何增强需求的类;
  • 配置类(@Configuration)仅在处理 @Bean 方法时可能代理,普通配置类本身不代理。

源码佐证

getAdvicesAndAdvisorsForBean 方法的核心逻辑是寻找匹配的拦截器 ,如果没有匹配的拦截器,直接返回 DO_NOT_PROXY,后续不会创建代理:

java 复制代码
if (advisors.isEmpty()) {
    // 无匹配的切面/拦截器,无需代理
    return DO_NOT_PROXY;
}

四、两种 AOP 创建场景对比:初始化阶段 vs 三级缓存

Spring 中存在 两种 AOP 代理创建场景 ,分别对应 正常 Bean循环依赖 Bean,两者的创建时机、逻辑和目的完全不同,核心差异如下表所示:

对比维度 正常 Bean(无循环依赖) 循环依赖 Bean(A 依赖 B、B 依赖 A)
创建时机 initializeBean 初始化最后一步 三级缓存 ObjectFactory 执行时(依赖注入阶段)
触发条件 正常 Bean 初始化完成,后置处理器触发 循环依赖发生,其他 Bean 提前获取早期引用
代理创建逻辑 标准 AOP 代理创建流程(wrapIfNecessary 早期代理创建流程(getEarlyBeanReference
是否所有 Bean 代理 仅匹配切点的 Bean 代理,按需创建 仅匹配切点的 Bean 代理,按需创建
代理对象唯一性 唯一,容器中仅存代理对象 唯一,最终与初始化阶段代理对象一致
三级缓存作用 仅存入工厂逻辑,未执行,最终删除 执行工厂逻辑,创建早期代理,移入二级缓存

1. 正常 Bean 的 AOP 流程(无循环依赖)

  1. 实例化原始 Bean 对象;
  2. 加入三级缓存(存入 ObjectFactory 工厂逻辑,未执行);
  3. 填充属性(从一级缓存获取其他成品 Bean);
  4. 执行初始化 initializeBean
  5. 初始化最后一步,触发 AnnotationAwareAspectJAutoProxyCreator按需创建 AOP 代理
  6. 将代理对象/原始对象放入一级缓存;
  7. 删除三级缓存(工厂逻辑从未执行);
  8. 全程未使用二级缓存。

2. 循环依赖 Bean 的 AOP 流程(A 依赖 B、B 依赖 A)

  1. 线程 1 实例化原始 A 对象,加入三级缓存(存入 A 的工厂逻辑);
  2. 线程 1 填充 A 属性,依赖 B,调用 getBean(B)
  3. 线程 1 实例化原始 B 对象,加入三级缓存(存入 B 的工厂逻辑);
  4. 线程 1 填充 B 属性,依赖 A,调用 getBean(A)
  5. 线程 1 从三级缓存获取 A 的工厂逻辑,执行 getEarlyBeanReference按需创建早期 AOP 代理
  6. 将早期代理对象放入二级缓存,删除三级缓存;
  7. B 获取到 A 的早期代理,完成属性填充和初始化,放入一级缓存;
  8. 线程 1 回到 A 的创建流程,完成 A 的属性填充;
  9. 初始化阶段,发现 A 已存在早期代理,跳过 AOP 代理创建
  10. 将 A 的早期代理对象放入一级缓存,删除二级缓存;
  11. 最终,A、B 均为成品代理对象,代理对象唯一,无替换风险

五、核心设计原理:为什么要分两个阶段创建 AOP?

1. 正常 Bean:初始化阶段创建 AOP 的必要性

AOP 代理的创建依赖于 完整的 Bean 实例 (属性已填充、初始化方法已执行)。如果在实例化后、属性填充前创建代理,会导致 代理对象包裹的原始 Bean 属性为 null,后续调用目标方法时会出现空指针异常。

因此,Spring 规定:AOP 代理必须在 Bean 属性完全填充、初始化方法执行完成后创建,这是保证代理对象可用性的核心前提。

2. 循环依赖 Bean:三级缓存提前创建 AOP 的必要性

循环依赖的核心矛盾是 "创建 A 需要 B,创建 B 需要 A" ,如果等待初始化阶段再创建 AOP 代理,会导致 死循环等待,无法完成 Bean 创建。

因此,Spring 通过三级缓存的 ObjectFactory 提前创建 早期代理对象 ,让循环依赖的 Bean 可以相互引用,同时通过 earlyProxyReferences 缓存保证 代理对象唯一性,避免初始化阶段重复创建代理。

3. 核心设计原则:延迟创建 + 唯一代理

Spring AOP 采用 "延迟创建代理" 原则,避免不必要的性能开销;同时通过 缓存机制 保证 代理对象唯一性,无论正常场景还是循环依赖场景,容器中最终只存在一个代理实例,避免对象替换导致的类型不一致、引用异常等问题。

六、源码总结:核心逻辑闭环

  1. 正常 Bean :AOP 代理在 initializeBean 方法的 applyBeanPostProcessorsAfterInitialization 阶段创建,由 AnnotationAwareAspectJAutoProxyCreator 触发,按需匹配切点,仅对符合条件的 Bean 代理
  2. 循环依赖 Bean :AOP 代理在三级缓存的 ObjectFactory 执行时创建,通过 getEarlyBeanReference 生成早期代理,仅为解决循环依赖,最终与初始化阶段代理对象一致
  3. 通用原则并非所有 Bean 都会被 AOP 代理,Spring 采用"按需代理"原则,通过切点匹配控制代理范围;
  4. 核心保障:全局锁、缓存机制、早期代理记录,保证并发场景下代理对象的唯一性和线程安全。

七、最终结论

  1. "在初始化阶段统一创建 AOP 代理"是 Spring AOP 的标准主流程,适用于 90% 以上的正常 Bean;
  2. 并非所有 Bean 都会进行 AOP 代理,仅对匹配切点(如事务、切面)的 Bean 按需创建;
  3. 循环依赖 Bean 的 AOP 代理是提前创建的早期代理,最终会与初始化阶段的代理对象保持一致,避免对象替换风险;
  4. 三级缓存的 AOP 工厂逻辑仅为解决循环依赖而设计,正常 Bean 场景下不会执行,仅为走过场。

这一设计既保证了 AOP 代理的正确性(基于完整 Bean 实例),又解决了循环依赖的核心矛盾,同时通过"按需代理"降低了框架性能开销,是 Spring 框架设计中"严谨性"与"灵活性"的完美体现。

相关推荐
Mr_Xuhhh2 小时前
LeetCode 热题 100 刷题笔记:从数组到字符串的经典解法(续)
java·数据结构·算法
皙然2 小时前
AQS模型详解:Java并发的核心同步框架(从原理到实战)
java·开发语言·jvm
愤豆2 小时前
08-Java语言核心-JVM原理-垃圾收集详解
java·开发语言·jvm
逸Y 仙X2 小时前
文章十四:ElasticSearch Reindex重建索引
java·大数据·数据库·elasticsearch·搜索引擎·全文检索
wregjru2 小时前
【读书笔记】Effective C++ 条款8:别让异常逃离析构函数
java·开发语言
烤麻辣烫2 小时前
I/O流 进阶流
java·开发语言·学习·intellij-idea
冷血~多好2 小时前
mysql实现主从复制以及springboot实现读写分离
java·数据库·mysql·springboot
山川行2 小时前
Python快速闯关专栏的总结
java·开发语言·笔记·python·算法·visual studio code·visual studio
默归2 小时前
Java云原生时代面临的挑战与变革
java·开发语言·云原生