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 小白变身编程高手!

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

相关推荐
言慢行善6 分钟前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星12 分钟前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟30 分钟前
操作系统之虚拟内存
java·服务器·网络
Tong Z32 分钟前
常见的限流算法和实现原理
java·开发语言
凭君语未可35 分钟前
Java 中的实现类是什么
java·开发语言
He少年37 分钟前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
前端大波1 小时前
前端面试通关包(2026版,完整版)
前端·面试·职场和发展
克里斯蒂亚诺更新1 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏4941 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构