Spring AOP 源码深度解析:从代理创建到通知执行的完整链路

Spring AOP 源码深度解析:从代理创建到通知执行的完整链路

在上一篇文章中,我们掌握了 Spring AOP 的基本用法和核心概念。但"知其然"之后,更要"知其所以然"。

今天,我们将深入 Spring Framework 源码(以 Spring 6.2.x 为例),一步步拆解 Spring AOP 的实现原理,揭开动态代理与通知链的神秘面纱。


一、入口:@EnableAspectJAutoProxy 做了什么?

当你在配置类上加上:

kotlin 复制代码
@EnableAspectJAutoProxy
public class AopConfig {}

这个注解的定义如下(简化):

less 复制代码
@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

typescript 复制代码
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(...) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    }
}

继续跟进,最终会注册一个名为 internalAutoProxyCreator 的 Bean,其实现类是:

AnnotationAwareAspectJAutoProxyCreator

这是整个 Spring AOP 的核心引擎,它继承自:

markdown 复制代码
AnnotationAwareAspectJAutoProxyCreator
 └── AspectJAwareAdvisorAutoProxyCreator
     └── AbstractAdvisorAutoProxyCreator
         └── AbstractAutoProxyCreator
             └── SmartInstantiationAwareBeanPostProcessor

也就是说,它是一个 BeanPostProcessor ------ 这意味着它能在每个 Bean 初始化前后进行干预!

二、代理对象何时创建?

AbstractAutoProxyCreator 重写了 postProcessAfterInitialization 方法:

typescript 复制代码
@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,则创建代理对象
typescript 复制代码
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() 最终会调用:

kotlin 复制代码
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()

例如,你写的:

java 复制代码
@Pointcut("execution(public * org.example.spring.aop.service..*.*(..))")

会被解析为一个 AspectJExpressionPointcut,其 matches() 方法会使用 AspectJ 表达式引擎判断目标方法是否匹配。

🔍 注意:Spring AOP 虽然使用 AspectJ 的注解和表达式语法,但匹配逻辑由 Spring 自己实现,并未依赖完整的 AspectJ 编译器。

四、代理对象如何创建?------ JDK Proxy vs CGLIB

createProxy() 方法内部会根据配置和目标类特性选择代理方式:

arduino 复制代码
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(...) 时,实际执行的是:

typescript 复制代码
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
@Before MethodBeforeAdviceInterceptor
@AfterReturning AfterReturningAdviceInterceptor
@Around 直接实现 MethodInterceptor
@AfterThrowing ThrowsAdviceInterceptor

这些 Interceptor 按照 通知类型 + 切面优先级 排序,形成一条链。

proceed() 的递归执行机制

ReflectiveMethodInvocation.proceed() 是典型的责任链递归实现:

java 复制代码
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 为例,执行顺序如下:

scss 复制代码
Around.before()
  → Before通知
    → 目标方法
  → AfterReturning通知
→ Around.after()

环绕通知(@Around)拥有最高控制权 :它可以决定是否调用 proceed(),甚至修改参数或返回值。

六、为什么"自调用"不触发 AOP?

考虑以下代码:

typescript 复制代码
@Service
public class OrderService {
    public void methodA() {
        this.methodB(); // ← 自调用!
    }

    @Transactional
    public void methodB() { ... }
}

▶ 根本原因:绕过了代理对象

  • Spring 容器中保存的是 代理对象(Proxy)
  • 但在 methodA() 内部,this 指向的是 原始的 OrderService 实例,而非代理
  • 因此 this.methodB() 直接调用原始方法,不会进入 JdkDynamicAopProxy.invoke()

▶ 解决方案

  1. 注入自身(推荐)

    typescript 复制代码
    @Autowired
    private OrderService self; // 注入的是代理对象
    
    public void methodA() {
      self.methodB(); // 通过代理调用
    }
  2. 使用 AopContext.currentProxy() (需开启暴露):

    typescript 复制代码
    @EnableAspectJAutoProxy(exposeProxy = true)
    public class AopConfig {}
    
    public void methodA() {
      ((OrderService) AopContext.currentProxy()).methodB();
    }

⚠️ 注意:第二种方式耦合了 AOP 框架,一般不推荐。

七、总结 Spring AOP 执行流程

scss 复制代码
[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 小白变身编程高手!

👉 点赞 + 关注,让更多小伙伴一起进步!

相关推荐
小光学长2 小时前
基于web的影视网站设计与实现14yj533o(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·前端·数据库
何中应2 小时前
【面试题-2】Java集合
java·开发语言·后端·面试题
BullSmall2 小时前
Tomcat SSL 配置及常见问题
java·tomcat·ssl
璞瑜无文2 小时前
Unity 游戏开发之方块随机生成(三)
java·unity·游戏引擎
周杰伦_Jay2 小时前
【JVM深度解析】运行时数据区+类加载+GC+调优实战(附参数示例)
java·jvm·spring boot·分布式·架构·java-ee
松莫莫2 小时前
【Spring Boot 实战】使用 Server-Sent Events (SSE) 实现实时消息推送
java·spring boot·后端
SoleMotive.2 小时前
springai和langchain4j的区别
java
子超兄2 小时前
GC/OOM问题处理思路
java·jvm
麒qiqi2 小时前
【Linux 系统编程核心】进程的本质、管理与核心操作
java·linux·服务器