Spring AOP源码解析:ProxyFactory如何选择JDK代理还是CGLIB
Spring AOP源码解析:ProxyFactory如何选择JDK代理还是CGLIB
适读人群:想搞清楚Spring AOP底层机制、彻底理解代理对象创建过程的Java开发者 | 阅读时长:约19分钟
开篇故事
有同学问我:为什么他的 @Transactional 加在 final 方法上不生效?我说这个问题要从 CGLIB 讲起。
他又问:CGLIB 和 JDK 动态代理有什么区别,Spring 是怎么选的?
我说这个问题要从 ProxyFactory 讲起。
这种"问题引发问题"的连锁反应,说明 Spring AOP 的底层知识是一个体系,不能孤立地只记结论。今天我们把 ProxyFactory 的选择逻辑完整过一遍,搞懂了这个,上面那些问题自然都有答案。
一、Spring AOP 的代理创建入口
Spring 创建代理对象的入口是 AbstractAutoProxyCreator,它是一个 BeanPostProcessor,在 Bean 初始化完成后(postProcessAfterInitialization)判断是否需要为这个 Bean 创建代理。
二、ProxyFactory 的选择逻辑
2.1 核心源码
决策逻辑在 DefaultAopProxyFactory.createAopProxy():
// DefaultAopProxyFactory.java
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 条件1:optimize=true(通常不设)
// 条件2:proxyTargetClass=true(@EnableAspectJAutoProxy(proxyTargetClass=true))
// 条件3:targetClass没有实现任何接口(或只实现了SpringProxy等内部接口)
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("No target class set");
}
// 即使强制要求CGLIB,如果目标类是接口,还是用JDK代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 目标类不是接口,用CGLIB
return new ObjenesisCglibAopProxy(config);
} else {
// 默认走JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
/**
* 判断是否没有用户实现的代理接口
* 如果只有 SpringProxy、Advised、DecoratingProxy 这些Spring内部接口,也算"没有接口"
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}2.2 决策树
2.3 什么时候用 CGLIB,什么时候用 JDK
| 场景 | 代理类型 | 原因 |
|---|---|---|
| 类没有实现任何接口 | CGLIB | JDK代理必须有接口 |
配置了 proxyTargetClass=true | CGLIB(优先) | 强制指定 |
| 类实现了接口,且没有特殊配置 | JDK动态代理 | Spring 默认行为 |
| Spring Boot 2.x 默认 | CGLIB | Boot改了默认值,proxyBeanMethods=true |
@FeignClient | JDK动态代理 | OpenFeign 自己管理,不走Spring AOP |
注意:Spring Boot 2.x 开始,默认 proxyTargetClass=true,也就是说即使你的类有接口,Spring Boot 也默认用 CGLIB。这个改变导致了一些以前用 JDK 代理没问题的代码,升级 Boot 后行为发生变化。
三、完整代码实现
3.1 手动使用 ProxyFactory
理解 Spring 的代理创建,最好的方式是自己手动写一遍:
package com.laozhang.aop.demo;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* 手动使用 ProxyFactory 创建代理
*/
public class ProxyFactoryDemo {
public static void main(String[] args) {
UserServiceImpl target = new UserServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
// 场景1:不设 proxyTargetClass,target有接口,走JDK代理
proxyFactory.addInterface(UserService.class);
// 添加环绕通知
proxyFactory.addAdvice((MethodInterceptor) invocation -> {
System.out.println("Before: " + invocation.getMethod().getName());
Object result = invocation.proceed();
System.out.println("After: " + invocation.getMethod().getName());
return result;
});
// 获取代理对象
UserService proxy = (UserService) proxyFactory.getProxy();
System.out.println("代理类型: " + proxy.getClass().getName());
// 输出:com.sun.proxy.$Proxy0(JDK代理)
proxy.createUser("老张");
// 场景2:强制用CGLIB
ProxyFactory cglibFactory = new ProxyFactory();
cglibFactory.setTarget(target);
cglibFactory.setProxyTargetClass(true); // 强制CGLIB
cglibFactory.addAdvice((MethodInterceptor) invocation -> {
System.out.println("[CGLIB] Before: " + invocation.getMethod().getName());
Object result = invocation.proceed();
System.out.println("[CGLIB] After: " + invocation.getMethod().getName());
return result;
});
UserService cglibProxy = (UserService) cglibFactory.getProxy();
System.out.println("CGLIB代理类型: " + cglibProxy.getClass().getName());
// 输出:com.laozhang.aop.demo.UserServiceImpl$$EnhancerBySpringCGLIB$$xxx(CGLIB代理)
}
}
interface UserService {
void createUser(String name);
}
class UserServiceImpl implements UserService {
@Override
public void createUser(String name) {
System.out.println("创建用户: " + name);
}
}3.2 Pointcut + Advisor 精确控制切面范围
package com.laozhang.aop.demo;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* 使用 Pointcut 精确匹配需要代理的方法
*/
public class PointcutAdvisorDemo {
public static void main(String[] args) {
OrderServiceImpl target = new OrderServiceImpl();
ProxyFactory factory = new ProxyFactory(target);
// 只代理带 @Transactional 注解的方法
Pointcut pointcut = new AnnotationMatchingPointcut(null, Transactional.class, true);
Advisor advisor = new DefaultPointcutAdvisor(pointcut, invocation -> {
System.out.println("[Transaction] 开始事务");
Object result;
try {
result = invocation.proceed();
System.out.println("[Transaction] 提交事务");
return result;
} catch (Throwable t) {
System.out.println("[Transaction] 回滚事务: " + t.getMessage());
throw t;
}
});
factory.addAdvisor(advisor);
OrderService proxy = (OrderService) factory.getProxy();
proxy.createOrder("ORDER-001"); // 有 @Transactional,切面生效
proxy.queryOrder("ORDER-001"); // 没有 @Transactional,切面不执行
}
}3.3 通过代码验证代理类型
package com.laozhang.aop.util;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
/**
* AOP 工具类,用于调试和验证代理类型
*/
public class AopDebugUtil {
public static void printProxyInfo(Object proxy) {
System.out.println("=== 代理信息 ===");
System.out.println("对象类型: " + proxy.getClass().getName());
System.out.println("是否是代理对象: " + AopUtils.isAopProxy(proxy));
System.out.println("是否是JDK代理: " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("是否是CGLIB代理: " + AopUtils.isCglibProxy(proxy));
if (AopUtils.isAopProxy(proxy)) {
// 获取代理的目标类
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(proxy);
System.out.println("目标类: " + targetClass.getName());
}
}
}四、踩坑实录
坑1:@Transactional 加在 final 方法上不生效
根因:CGLIB 通过继承目标类创建子类,final 方法不能被子类覆盖,所以 CGLIB 无法代理 final 方法。
验证代码:
@Service
public class OrderService {
// CGLIB无法代理这个方法!@Transactional完全无效!
@Transactional
public final void createOrder(CreateOrderRequest req) {
// 事务根本没开
orderMapper.insert(req);
}
}解决:去掉 final,或者提取接口改用 JDK 代理。
坑2:proxyTargetClass=true 导致接口注入报错
症状:@Autowired 注入时报 NoSuchBeanDefinitionException: No qualifying bean of type 'UserService'。
根因:proxyTargetClass=true 时,Spring 创建的是 UserServiceImpl 的 CGLIB 子类,不是 UserService 接口的实现(虽然它继承了),在某些场景下按接口类型查找找不到。
解决:优先用实现类类型注入,或者显式声明代理接口:
// 按实现类注入(CGLIB代理时推荐)
@Autowired
private UserServiceImpl userService;
// 或者按接口注入(JDK代理时正常工作)
@Autowired
private UserService userService;坑3:同一个类的两个方法互相调用,切面只触发一次
这是上一篇也提到的"自调用问题",根源在于代理机制:
@Service
public class OrderService {
@Transactional // 这个@Transactional没用!
public void methodA() {
// this 指的是原始对象,不是代理对象
this.methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// ...
}
}调用 proxy.methodA() → 代理处理 methodA 的事务 → this.methodB() 绕过代理 → methodB 的 REQUIRES_NEW 没有生效。
坑4:@EnableAspectJAutoProxy 和 Spring Boot 默认行为冲突
症状:手动加了 @EnableAspectJAutoProxy(proxyTargetClass=false),想用 JDK 代理,但还是走 CGLIB。
根因:Spring Boot 的 AopAutoConfiguration 默认设置了 spring.aop.proxy-target-class=true,覆盖了注解的设置。
解决:在 application.yml 里覆盖:
spring:
aop:
proxy-target-class: false # 改回JDK代理优先五、总结与延伸
ProxyFactory 的选择逻辑一句话总结:有接口且没强制 CGLIB → JDK 代理;其余 → CGLIB。
Spring Boot 2.x 改变了默认行为,现在即使有接口也默认用 CGLIB,这让 CGLIB 成了绝大多数场景的选择。
CGLIB 的两个核心限制要牢记:
- 目标类不能是
final - 代理的方法不能是
final、static、private
下一篇(460)会深入 JDK 动态代理和 CGLIB 的字节码生成机制,从本质上理解两者的性能差异和适用场景。
