AOP - 动态代理

AOP 动态代理源码分析

Spring AOP 解决的问题:把日志、事务、鉴权这类横切逻辑从业务代码里抽出来,不改字节码、不动源文件,运行期给目标 bean 套一层代理。
方法调用先进代理,代理把匹配的通知组织成拦截器链依次执行,最内层才是真实方法。织入靠 BeanPostProcessor 在初始化后置阶段偷梁换柱:容器里存的从此是代理,不是原对象。

三段式看完整链路:织入入口(什么时候换成代理)→ 技术选型(JDK 还是 CGLIB)→ 运行期执行(拦截器链怎么转)。

// 基于本地 Spring 源码 (D:/1ForCode/SourceCode/JavaSourceReadingLab, spring-aop 5.3.x),
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    @Nullable
    protected static final Object[] DO_NOT_PROXY = null;    // 「不代理」就是 null,子类切点不匹配时返回它
    // ...

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {     // 循环依赖专用入口:三级缓存的 ObjectFactory 会调它提前出代理
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);      // 记账:这个 bean 已经提前代理过
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
    // ...

    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {  // AOP 织入主入口:bean 初始化完成之后
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {   // 循环依赖时已提前代理过 -> 这里跳过,避免包两层
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    // ...

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {    // 之前已判定不代理,直接短路
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);     // Advice/Advisor/Pointcut 这些 AOP 基础设施自己不能被代理,否则鸡生蛋
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);  // 切点匹配:筛出适用本 bean 的全部 Advisor
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));  // 原始 bean 被包进 TargetSource,藏到代理身后
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;       // 返回值替换原 bean 进容器,注入到别处的都是这个代理
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // ...

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        // ...
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);    // 把 proxyTargetClass 等全局代理配置抄过来
        // ... proxyTargetClass 未强制时:shouldProxyTargetClass / evaluateProxyInterfaces 决定走类代理还是接口代理

        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        // ...
        return proxyFactory.getProxy(classLoader);      // 内部委托 DefaultAopProxyFactory.createAopProxy,JDK/CGLIB 在那分叉
    }
}
// 同仓, org.springframework.aop.framework.DefaultAopProxyFactory
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    // ...
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!NativeDetector.inNativeImage() &&      // GraalVM native image 里没法运行期生成类,强制走 JDK 代理
                (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
                return new JdkDynamicAopProxy(config);      // 目标本身是接口/已是 JDK 代理/lambda:CGLIB 无法继承它们,回退 JDK
            }
            return new ObjenesisCglibAopProxy(config);      // CGLIB:生成目标子类;Objenesis 绕过构造器实例化代理对象
        }
        else {
            return new JdkDynamicAopProxy(config);      // 有用户接口且没强制类代理:JDK 动态代理
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));     // Spring 自己塞的 SpringProxy 标记接口不算「用户接口」
    }
}
// 同仓, org.springframework.aop.framework 下的 JdkDynamicAopProxy / AdvisedSupport /
// ReflectiveMethodInvocation / CglibAopProxy
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    // ...
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        // ...
        return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);     // 自己就是 InvocationHandler:一个代理配一个 handler 实例
    }
    // ...

    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                return equals(args[0]);     // 接口没声明 equals/hashCode 时不走增强:比较的是代理配置,不是 target
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                return hashCode();
            }
            // ... DecoratingProxy / Advised 接口的管理方法:直接转发给代理配置,不进拦截器链

            Object retVal;

            if (this.advised.exposeProxy) {
                oldProxy = AopContext.setCurrentProxy(proxy);       // 把代理挂进 ThreadLocal:给「类内自调用不走代理」留的官方逃生门
                setProxyContext = true;
            }

            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();      // 每次调用才取 target,不在构造时存死:TargetSource 可能是池化/热替换实现
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // Get the interception chain for this method.
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);    // 本方法没命中任何通知:省掉 MethodInvocation 对象,直接反射
            }
            else {
                MethodInvocation invocation =
                        new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                retVal = invocation.proceed();      // 启动责任链,递归见下方 proceed()
            }

            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                retVal = proxy;     // 方法返回 this 时偷换成代理:否则裸 target 逃逸出去,后续链式调用全部失去增强
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);   // 环绕通知不调 proceed 又返回 null,基本类型没法拆箱,早炸早发现
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);     // 池化 TargetSource 要归还,对应上面的 getTarget
            }
            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);   // 还原而非清空:代理调代理时外层上下文不能丢
            }
        }
    }
}

// ---- AdvisedSupport:每个方法的拦截器链算一次就缓存 ----
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass);     // 真正的切点匹配 + Advice 适配成 MethodInterceptor,只做这一次
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}

// ---- ReflectiveMethodInvocation:责任链的推进器 ----
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    // ...
    protected final List<?> interceptorsAndDynamicMethodMatchers;

    private int currentInterceptorIndex = -1;   // 进度存在 invocation 里而不是迭代器里:proceed 会被用户通知重入式回调

    @Override
    @Nullable
    public Object proceed() throws Throwable {
        // We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();       // 链走到尾:洋葱最内核,反射调真实方法
        }

        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)) {   // 动态切点(如 args())要看实参,只能留到每次调用时匹配
                return dm.interceptor.invoke(this);
            }
            else {
                return proceed();       // 本层动态匹配失败:跳过,递归进下一层
            }
        }
        else {
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  // 把 this 交给拦截器,它内部再调 mi.proceed() 就进入下一层
        }
    }

    @Nullable
    protected Object invokeJoinpoint() throws Throwable {
        return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
    }
}

// ---- CglibAopProxy.DynamicAdvisedInterceptor:CGLIB 路线的运行期入口,与 JDK invoke 同构 ----
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
    // ...
    @Override
    @Nullable
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // ... exposeProxy / targetSource.getTarget(),与 JDK invoke 完全同构
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = invokeMethod(target, method, argsToUse, methodProxy);      // 无通知:走 MethodProxy(FastClass 索引直调),连反射都省了
        }
        else {
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();     // 有通知:两条技术路线在 proceed() 汇合
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
        // ... finally: releaseTarget / 还原 AopContext
    }
}

原理串讲

以一个挂了 @Around 通知的 @Service bean 走一遍完整链路。
容器启动阶段,AnnotationAwareAspectJAutoProxyCreator(AbstractAutoProxyCreator 的子类)作为 BeanPostProcessor 先注册进容器;
目标 bean 实例化、属性填充、初始化完成后进 postProcessAfterInitialization,转 wrapIfNecessary:先用 advisedBeans 缓存短路已判定不代理的 bean,跳过 AOP 基础设施类,再由子类实现的 getAdvicesAndAdvisorsForBean 拿容器里全量 Advisor 逐个做切点静态匹配。
命中则 createProxy:目标包成 SingletonTargetSource,连同 Advisor 列表和代理配置装进一个 ProxyFactory,
getProxy 内部委托 DefaultAopProxyFactory.createAopProxy 分叉——有用户接口且没强制 proxyTargetClassJdkDynamicAopProxy,否则 ObjenesisCglibAopProxy
wrapIfNecessary 的返回值替换原 bean 进入一级缓存,之后所有注入点拿到的都是代理。

运行期调 proxy.doSomething(),进 JdkDynamicAopProxy.invoke(CGLIB 则是 DynamicAdvisedInterceptor.intercept):
放行 equals/hashCode 等特殊方法后,getInterceptorsAndDynamicInterceptionAdvice 取本方法的拦截器链——首次由 DefaultAdvisorChainFactory 完成切点到方法级的匹配、并把前置/后置等各类 Advice 统一适配成 MethodInterceptor,之后 methodCache 按 Method 直查。

代码块JAVA · 2 行收起展开
链空就直接反射调目标;否则 new 一个 `ReflectiveMethodInvocation` 并 `proceed()`。
`proceed` 用 `++currentInterceptorIndex` 取出下一个拦截器,把 this 传给它的 `invoke(mi)`;环绕通知的用户代码在里面再调 `mi.proceed()` 就又回到这个函数、进入下一层,直到下标走到链尾 `invokeJoinpoint()` 反射调真实方法,返回值再沿调用栈逐层退出——这就是「前置逻辑 → proceed → 后置逻辑」的洋葱模型。

为什么责任链做成「invocation 持有下标 + 拦截器反调 proceed」的递归,而不是一个 for 循环?因为环绕通知必须拿到控制权:用户代码可以不调 proceed()(短路目标方法)、调多次(重试)、改完参数再调,for 循环里做不到这种控制反转;下标存在 invocation 对象里而不是用迭代器,也是为了 invocableClone() 克隆后各自独立推进。
为什么拦截器链要按 Method 缓存?AspectJ 切点表达式求值很贵,而一个代理的 Advisor 集合建好后基本不变,所以匹配结果一次算完进 methodCache,只有依赖实参的动态切点降级为 InterceptorAndDynamicMethodMatcher,留到每次 proceed 时补一次 matches
为什么 target 要等到 invoke 里才 targetSource.getTarget()?因为代理只认 TargetSource 这个扩展点,池化、热替换、原型目标都靠它实现,SingletonTargetSource 只是最常见的特例。

AOP 与循环依赖的交点:若被代理 bean 陷入循环依赖,三级缓存暴露的 ObjectFactory 会提前调 getEarlyBeanReference 生成代理并在 earlyProxyReferences 记账,
之后 postProcessAfterInitializationremove(cacheKey) != bean 发现已代理过就不再包第二层,保证注入出去的引用和容器里最终的是同一个代理(见 循环依赖-三级缓存getEarlyBeanReference)。
@Transactional 就是一个从这条链路织入的环绕通知——TransactionInterceptor 也是 MethodInterceptor,排在拦截器链上,见 事务-Transactional

设计取舍

  • JDK 代理基于 Proxy + InvocationHandler,只能增强接口方法;CGLIB 用 ASM 生成子类,final 类、final/private 方法进不了代理。
    SpringBoot 2.x 起默认 proxyTargetClass=true 全走 CGLIB,省掉「必须有接口」的心智负担。
  • 自调用不走代理:this.otherMethod() 用的是裸 this,不经过拦截器链,这是 @Transactional/@Async 失效的头号原因;exposeProxy=true + AopContext.currentProxy() 是官方补救。
  • 织入发生在初始化后置阶段,所以 BeanPostProcessor 自身和 Advisor 等基础设施 bean 天然不能被代理(isInfrastructureClass 直接跳过)。
  • 未命中通知的方法近似零开销:JDK 路线直接反射,CGLIB 路线走 FastClass 直调,都不创建 MethodInvocation。
  • CGLIB 用 Objenesis 实例化代理,目标构造器不会被执行第二次;但代理子类的字段是自己的一份,通过代理直接读字段拿不到 target 的值——增强只对方法调用生效。

延伸阅读