Spring 用的是什么动态代理?它是怎么配置的?
程序员面试资料大全|各种技术书籍等资料-1000G
一、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(); // 不走代理!}@Transactionalpublic void validate() {...}
}
解决方案:
// 方案1:自我注入(推荐)
public class OrderService {@Autowiredprivate 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) // 强制CGLIBpublic ReportService reportService() {return new ReportService();}
}
2. 自定义代理处理器
public class CustomProxyProcessor extends AbstractAutoProxyCreator {@Overrideprotected 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)
程序员面试资料大全|各种技术书籍等资料-1000G