Spring源码 第四篇:Spring 5 源码深度拆解:AOP 全流程核心原理

一、先搞懂核心前提:Spring AOP 底层基础

1. AOP 核心概念

概念 源码对应 核心含义
切面 Aspect @Aspect 标注的类 切点 + 通知的集合,定义横向逻辑的类
切点 Pointcut @Pointcut 标注的方法 定义哪些类/方法需要被拦截增强
通知 Advice @Before/@Around/@After 拦截到方法后,要执行的横向逻辑
通知器 Advisor Advisor 接口 Spring AOP 中切点(Pointcut)与通知(Advice)的封装单元,是 Spring 判定 Bean 是否需要代理、如何代理的核心依据,是 AOP 执行的最小逻辑单元
动态代理 JdkDynamicAopProxy / CglibAopProxy AOP 的底层实现,生成代理对象替代原生 Bean

2. 两种动态代理的核心区别

Spring AOP 仅支持两种动态代理,源码中会自动选择:

特性 JDK 动态代理 CGLIB 动态代理
底层实现 基于接口,生成实现类的代理 基于继承,生成目标类的子类代理
代理对象生成
方法执行效率 低(反射调用) 高(字节码生成,无反射)
Spring 默认选择 目标类实现了接口时优先使用 目标类无接口时使用

二、AOP 开启的入口:@EnableAspectJAutoProxy

我们使用 Spring AOP 时,只需要加一个注解:

java 复制代码
@Configuration
@EnableAspectJAutoProxy // 开启AOP
@ComponentScan("com.example")
public class SpringConfig {
}

这个注解,就是 AOP 所有逻辑的起点。

1. 注解源码拆解

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class) // 核心:导入了注册器
public @interface EnableAspectJAutoProxy {
    // 强制使用CGLIB代理,默认false
    boolean proxyTargetClass() default false;
    // 暴露代理对象到ThreadLocal,解决内部调用AOP失效问题,默认false
    boolean exposeProxy() default false;
}

2. 核心:AspectJAutoProxyRegistrar 做了什么?

这个注册器的核心作用,就是向 Spring 容器中注册了一个核心 BeanPostProcessor:AnnotationAwareAspectJAutoProxyCreator

关键源码

java 复制代码
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 核心:注册 AnnotationAwareAspectJAutoProxyCreator
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        // 处理 proxyTargetClass、exposeProxy 配置
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}

3. 灵魂核心:AnnotationAwareAspectJAutoProxyCreator

这个类,是 Spring AOP 的绝对核心,它的继承关系:

复制代码
AnnotationAwareAspectJAutoProxyCreator
        ↓
AbstractAdvisorAutoProxyCreator
        ↓
AbstractAutoProxyCreator
        ↓
实现了 BeanPostProcessor 接口

为什么它是核心?

  • 它是一个 BeanPostProcessor,会介入每一个 Bean 的生命周期
  • 它负责:扫描解析所有 @Aspect 切面,生成 Advisor
  • 它负责:判断 Bean 是否需要生成代理,生成代理对象
  • 它就是我们第二篇 Bean 生命周期里,后置处理器中生成 AOP 代理的那个类

三、AOP 全流程第一阶段:容器启动时,切面解析与 Advisor 生成

Spring AOP 不是 Bean 创建时才解析切面,而是容器启动时,就提前扫描解析所有切面,生成 Advisor 通知器,对应我们第一篇的容器启动流程。

完整流程

  1. 容器启动,执行 refresh()
  2. 执行 invokeBeanFactoryPostProcessors(),解析 @EnableAspectJAutoProxy,注册 AnnotationAwareAspectJAutoProxyCreator
  3. 执行 registerBeanPostProcessors(),把 AnnotationAwareAspectJAutoProxyCreator 实例化,加入到 BeanPostProcessor 列表中
  4. AnnotationAwareAspectJAutoProxyCreator 提前扫描所有 @Aspect 注解的类,解析切点、通知,封装成一个个 Advisor 对象,缓存起来,供后续 Bean 创建时使用

核心源码:切面解析

java 复制代码
// AnnotationAwareAspectJAutoProxyCreator 核心方法
protected List<Advisor> findCandidateAdvisors() {
    // 1. 先拿到父类的Advisor
    List<Advisor> advisors = super.findCandidateAdvisors();
    // 2. 核心:扫描所有@Aspect注解的类,解析生成Advisor
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    return advisors;
}

关键细节

  • 每个 @Before/@Around/@After 等通知,都会被封装成一个独立的 Advice(通知);Advice 与对应的 Pointcut(切点)组合,形成 Advisor(通知器)。若多个通知关联同一个切点,会生成多个包含该切点的 Advisor,执行顺序由 @Order 控制
  • 每个 Advisor 都包含:切点表达式(匹配哪些方法)+ 通知逻辑(要执行的代码)
  • 解析完成后,所有 Advisor 会被缓存起来,后续每个 Bean 创建时,直接用这些 Advisor 匹配,判断是否需要生成代理

四、AOP 全流程第二阶段:Bean 创建时,代理对象生成

核心结论先记死:

AOP 代理对象,生成于 Bean 生命周期的「BeanPostProcessor 后置处理阶段」,也就是 initializeBean 方法的最后一步。

完整流程(对应 Bean 生命周期)

复制代码
doGetBean → createBean → doCreateBean
        ↓
1. createBeanInstance:实例化原生Bean
        ↓
2. populateBean:填充属性,依赖注入
        ↓
3. initializeBean:初始化
   • Aware接口执行
   • BeanPostProcessor前置处理
   • 初始化方法执行
   • BeanPostProcessor后置处理【AOP代理生成就在这里!】
        ↓
4. 代理对象生成,替换原生Bean,放入单例池

1. 入口:后置处理方法

java 复制代码
// AbstractAutoProxyCreator(父类)
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 核心:判断是否需要生成代理,需要则返回代理对象,否则返回原生Bean
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
    return bean;
}

2. 核心判断:wrapIfNecessary(要不要生成代理)

java 复制代码
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 1. 提前判断:是否已经处理过,或者是切面类本身,不需要代理
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (this.advisedBeans.containsKey(cacheKey)) {
        return this.advisedBeans.get(cacheKey);
    }
   // 2. 基础设施类(如 @Aspect 标注的切面类)或需跳过的 Bean,直接返回原生对象
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // 3. 核心:匹配Advisor,判断当前Bean是否有匹配的切面
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 4. 有匹配的切面 → 生成代理对象
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // ↓↓↓ 生成代理对象 ↓↓↓
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回代理对象,替换原生Bean
        return proxy;
    }
    // 5. 没有匹配的切面 → 返回原生Bean
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

3. 生成代理:createProxy

java 复制代码
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    // 1. 封装代理配置
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    // 处理proxyTargetClass配置,决定用JDK还是CGLIB
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        } else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    // 2. 把Advisor加入代理工厂
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    // 3. 核心:生成代理对象
    return proxyFactory.getProxy(getBeanClassLoader());
}

4. 代理选择逻辑:JDK 还是 CGLIB?

java 复制代码
// DefaultAopProxyFactory
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 1. 强制CGLIB、或者目标类无接口 → 用CGLIB
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    // 2. 目标类实现了接口 → 用JDK动态代理
    else {
        return new JdkDynamicAopProxy(config);
    }
}

五、循环依赖场景下,AOP 代理的特殊处理

前面我们介绍循环依赖时,提到了 getEarlyBeanReference 方法,这个方法就是循环依赖场景下,提前生成 AOP 代理的核心。

问题场景

A ↔ B 循环依赖,且 A 需要生成 AOP 代理:

  1. A 实例化后,暴露工厂到三级缓存
  2. B 创建时,需要注入 A,从三级缓存拿到 A 的工厂,执行 getEarlyBeanReference
  3. 这里会提前生成 A 的 AOP 代理对象,注入到 B 中
  4. A 后续初始化完成后,不会再重复生成代理,保证单例

核心源码

java 复制代码
// AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 提前生成代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

关键细节

  • 循环依赖场景下,代理对象会提前在 getEarlyBeanReference 中生成(注入给依赖方),同时将原生 Bean 存入 earlyProxyReferences 缓存
  • 后续执行 postProcessAfterInitialization 时,会优先检查该缓存:若存在当前 Bean 的记录,则直接返回已生成的代理对象,避免重复代理
  • 这就是为什么三级缓存要存工厂,而不是直接存 Bean 引用 ------ 为了支持循环依赖下的 AOP 代理提前生成

六、AOP 全流程第三阶段:运行时,通知链执行

代理对象生成后,当我们调用代理对象的方法时,就会触发切面通知的执行,Spring 通过责任链模式控制通知的执行顺序。

核心执行流程

  1. 调用代理对象的目标方法
  2. 进入代理对象的拦截器(JDK 的 InvocationHandler / CGLIB 的 MethodInterceptor
  3. 匹配当前方法的所有 Advisor,生成通知链(Interceptor 链)
  4. 通过 ReflectiveMethodInvocationproceed() 方法,递归执行通知链
  5. 执行顺序:@Around 前置 → @Before → 目标方法 → (@AfterReturning / @AfterThrowing)→ @After@Around 后置。

@After 是最终通知,无论目标方法是否异常,都会在 @AfterReturning@AfterThrowing 之后执行。

核心源码:通知链执行

java 复制代码
// ReflectiveMethodInvocation
@Override
@Nullable
public Object proceed() throws Throwable {
    // 1. 通知链执行完毕,执行目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // 2. 拿到下一个拦截器,递归执行
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 不匹配,跳过,执行下一个
            return proceed();
        }
    }
    else {
        // 执行拦截器(通知逻辑)
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

七、面试高频问题

1. AOP 代理生成在 Bean 生命周期的哪个阶段?

:正常场景下,生成于 Bean 生命周期「初始化阶段」的最后一步,即 BeanPostProcessor 的后置处理方法 postProcessAfterInitialization,该方法在初始化方法(@PostConstruct/init-method)执行完成后调用。

2. JDK 动态代理和 CGLIB 动态代理的核心区别?

  • JDK 动态代理基于接口实现,要求目标类必须实现接口,生成接口的实现类代理;CGLIB 基于继承实现,生成目标类的子类代理,要求目标类不能被 final 修饰。
  • JDK 动态代理通过反射执行方法,在极高并发场景下性能略低于 CGLIB,常规业务场景差异不大。
  • Spring 默认策略:目标类实现了接口用 JDK 代理,无接口用 CGLIB 代理,可通过 proxyTargetClass=true 强制使用 CGLIB。

3. Spring AOP 内部调用为什么会失效?怎么解决?

:因为内部调用时,调用的是 this 原生对象,不是代理对象,不会触发切面逻辑。

解决方法

  1. 开启 @EnableAspectJAutoProxy(exposeProxy = true)
  2. 内部调用时,用 ((当前类) AopContext.currentProxy()).目标方法(),通过代理对象调用。

4. @Aspect 切面的执行顺序怎么控制?

:通过 @Order 注解控制,value 值越小,优先级越高,执行顺序越靠前。前置通知优先级高的先执

相关推荐
better_liang1 小时前
每日Java面试场景题知识点之-SpringBoot启动流程
java·面试·springboot·源码解析·启动流程
RyFit1 小时前
Java + AI 实战:Spring AI 从入门到企业级落地
java·人工智能·spring
ServBay2 小时前
2026 Mac 本地大模型部署深度解析与混合架构指南
后端·macos·aigc
一拳一个娘娘腔2 小时前
【SRC漏洞挖掘系列】第10期:GraphQL & API 安全 —— 现代 API 的“裸奔”时代
后端·安全·graphql
ZhengEnCi3 小时前
01-如何监听接口调用情况?
java·spring boot·后端
JAVA面经实录9174 小时前
MyBatis学习体系
java·mybatis
java1234_小锋4 小时前
在 Spring AI 中如何实现函数调用(Function Calling)?请说明其基本原理和应用场景。
java·人工智能·spring
小马爱打代码4 小时前
Spring源码 第九篇:Spring 5 源码深度拆解 - Spring 事件驱动模型
java·后端·spring
ForgeAI码匠5 小时前
ForgeAdmin|Spring Boot 3 后台框架的自动配置设计:少写配置,多做组合
java·spring boot·后端