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 分叉——有用户接口且没强制 proxyTargetClass 就 JdkDynamicAopProxy,否则 ObjenesisCglibAopProxy。
wrapIfNecessary 的返回值替换原 bean 进入一级缓存,之后所有注入点拿到的都是代理。
运行期调 proxy.doSomething(),进 JdkDynamicAopProxy.invoke(CGLIB 则是 DynamicAdvisedInterceptor.intercept):
放行 equals/hashCode 等特殊方法后,getInterceptorsAndDynamicInterceptionAdvice 取本方法的拦截器链——首次由 DefaultAdvisorChainFactory 完成切点到方法级的匹配、并把前置/后置等各类 Advice 统一适配成 MethodInterceptor,之后 methodCache 按 Method 直查。
代码块收起展开
链空就直接反射调目标;否则 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 记账,
之后 postProcessAfterInitialization 里 remove(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 的值——增强只对方法调用生效。