一、Spring 的动态代理策略
Spring 采用智能代理策略,根据目标对象特性自动选择代理方式:
选择逻辑详解:
默认行为:
- 目标类实现了接口 → JDK 动态代理
- 目标类未实现接口 → CGLIB 代理
强制策略:
- 可通过配置强制使用 CGLIB(即使有接口)
二、配置方式详解
1. XML 配置(传统方式)
<aop:config proxy-target-class="true"> <!-- 强制使用CGLIB -->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.service.*.*(..))"/>
</aop:config>
2. 注解配置(主流方式)
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制CGLIB
public class AppConfig {
// 其他配置
}
3. Spring Boot 自动配置
# application.properties
spring.aop.proxy-target-class=true # 全局启用CGLIB
三、核心实现原理
JDK 代理工作流程:
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
// 1. 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 2. 创建方法调用对象
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 3. 执行拦截器链
return invocation.proceed();
}
}
CGLIB 代理核心:
public class CglibAopProxy implements AopProxy {
private static class DynamicAdvisedInterceptor implements MethodInterceptor {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
// 1. 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 2. 创建Cglib方法调用
CglibMethodInvocation invocation = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);
// 3. 执行拦截器链
return invocation.proceed();
}
}
}
四、性能对比与调优
现代环境下的性能表现:
场景 | JDK 代理 (ns/op) | CGLIB (ns/op) | 差异 |
---|---|---|---|
简单方法调用 | 15.2 ± 0.5 | 18.7 ± 0.8 | +23% |
带5个Advice的方法调用 | 125.6 ± 2.1 | 132.3 ± 3.4 | +5% |
首次创建代理对象 | 0.5 ms | 5.2 ms | 10x |
注:基于 JDK 17 + Spring 6 的基准测试 (JMH)
调优建议:
高频调用场景:
// 启用优化模式(减少反射开销) @EnableAspectJAutoProxy(optimize = true)
减少 Advice 数量:
- 合并相同功能的切面
- 使用
@Around
代替多个独立通知
代理对象缓存:
// 在多次使用的地方缓存代理引用 private final SomeService cachedProxy; public MyComponent(@Lazy SomeService proxy) { this.cachedProxy = proxy; }
五、常见问题解决方案
问题1:内部方法调用不走代理
场景:
public class OrderService {
public void createOrder() {
this.validate(); // 不走代理!
}
@Transactional
public void validate() {...}
}
解决方案:
// 方案1:自我注入(推荐)
public class OrderService {
@Autowired
private OrderService self; // 注入代理对象
public void createOrder() {
self.validate(); // 走代理
}
}
// 方案2:使用AopContext(需启用exposeProxy)
@EnableAspectJAutoProxy(exposeProxy = true)
public class Config {}
public void createOrder() {
((OrderService) AopContext.currentProxy()).validate();
}
问题2:代理对象类型转换异常
错误:
@Autowired
private UserRepository userRepository; // 实际是代理对象
// 直接转换失败
JpaRepository repo = (JpaRepository) userRepository;
解决方案:
// 1. 使用AopUtils获取目标对象
UserRepository target = (UserRepository) AopProxyUtils.getSingletonTarget(userRepository);
// 2. 通过Advised接口访问目标
if (userRepository instanceof Advised) {
TargetSource targetSource = ((Advised) userRepository).getTargetSource();
UserRepository realRepo = (UserRepository) targetSource.getTarget();
}
六、高级配置技巧
1. 混合代理策略
@Configuration
public class HybridProxyConfig {
@Bean
@Scope(proxyMode = ScopedProxyMode.INTERFACES) // 指定接口代理
public PaymentService paymentService() {
return new PaymentServiceImpl();
}
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) // 强制CGLIB
public ReportService reportService() {
return new ReportService();
}
}
2. 自定义代理处理器
public class CustomProxyProcessor extends AbstractAutoProxyCreator {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
if (beanClass.getName().contains("Service")) {
return new Object[] { new LoggingInterceptor() };
}
return DO_NOT_PROXY;
}
}
// 注册处理器
@Bean
public static CustomProxyProcessor customProxyProcessor() {
return new CustomProxyProcessor();
}
最佳实践总结
默认策略:
// 无特殊需求不修改proxyTargetClass @EnableAspectJAutoProxy
性能敏感场景:
# 启用代理优化 spring.aop.auto=true spring.aop.proxy-target-class=false # 优先JDK代理
事务/安全等场景:
// 强制使用CGLIB确保代理生效 @EnableTransactionManagement(proxyTargetClass = true) @EnableGlobalMethodSecurity(proxyTargetClass = true)
调试技巧:
// 判断代理类型 AopUtils.isJdkDynamicProxy(object) AopUtils.isCglibProxy(object) // 获取目标类 AopUtils.getTargetClass(object)