当前位置: 首页 > news >正文

Spring 用的是什么动态代理?它是怎么配置的?

程序员面试资料大全|各种技术书籍等资料-1000G

一、Spring 的动态代理策略

Spring 采用智能代理策略,根据目标对象特性自动选择代理方式:

有接口
无接口
目标对象
是否有接口?
JDK 动态代理
CGLIB 代理
创建接口代理
创建子类代理

选择逻辑详解:

  1. 默认行为

    • 目标类实现了接口 → JDK 动态代理
    • 目标类未实现接口 → CGLIB 代理
  2. 强制策略

    • 可通过配置强制使用 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.518.7 ± 0.8+23%
带5个Advice的方法调用125.6 ± 2.1132.3 ± 3.4+5%
首次创建代理对象0.5 ms5.2 ms10x

:基于 JDK 17 + Spring 6 的基准测试 (JMH)

调优建议:

  1. 高频调用场景

    // 启用优化模式(减少反射开销)
    @EnableAspectJAutoProxy(optimize = true)
    
  2. 减少 Advice 数量

    • 合并相同功能的切面
    • 使用 @Around 代替多个独立通知
  3. 代理对象缓存

    // 在多次使用的地方缓存代理引用
    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();
}

最佳实践总结

  1. 默认策略

    // 无特殊需求不修改proxyTargetClass
    @EnableAspectJAutoProxy
    
  2. 性能敏感场景

    # 启用代理优化
    spring.aop.auto=true
    spring.aop.proxy-target-class=false # 优先JDK代理
    
  3. 事务/安全等场景

    // 强制使用CGLIB确保代理生效
    @EnableTransactionManagement(proxyTargetClass = true)
    @EnableGlobalMethodSecurity(proxyTargetClass = true)
    
  4. 调试技巧

    // 判断代理类型
    AopUtils.isJdkDynamicProxy(object) 
    AopUtils.isCglibProxy(object)// 获取目标类
    AopUtils.getTargetClass(object)
    

程序员面试资料大全|各种技术书籍等资料-1000G

在这里插入图片描述

http://www.lqws.cn/news/522451.html

相关文章:

  • fluentd + elasticsearch + grafana 不能显示问题
  • 16.7 Prometheus+Grafana实战:容器化监控与日志聚合一站式解决方案
  • Linux远程机器无法连接-------解决方案
  • 开源代码修复新标杆——月之暗面最新开源编程模型Kimi-Dev-72B本地部署教程,自博弈修复 Bug
  • AI知识库搭建需要的开源技术方案
  • C#.Net筑基-优雅LINQ的查询艺术
  • 基于Hp感染的慢性胃炎居家管理小程序的设计与实现(消息震动)
  • 连锁企业网络互联方案对比:MPLS专线 vs 内网穿透
  • MySQL为什么默认引擎是InnoDB?
  • 永磁同步电机无速度算法--基于三角函数锁相环的滑模观测器
  • django 中间件
  • 打造丝滑的Android应用:LiveData完全教程
  • YOLOv8 改进点详解
  • 联邦学习中的本地迭代误差与全局聚合误差
  • CSMatIO库的安装与C#实现.mat文件生成
  • 数据库系统总结
  • cocos2 实现全局音量管理
  • 51c嵌入式~CAN~合集2
  • 数据文件写入技术详解:从CSV到Excel的ETL流程优化
  • 使用AkShare获取股票报表
  • [省选联考 2025] 推箱子
  • Java 的强制类型转换
  • Sortablejs动态同类型穿插
  • npm 报错:“无法加载文件 ...npm.ps1,因为在此系统上禁止运行脚本” 解决方案(附执行策略说明)
  • 创新让生活更美好丨“鑫亘科技亮相2025上海CMEF,创新医疗材料引领未来!”
  • 【Docker基础】Docker容器管理:docker pause、stop、kill区别
  • Gemini 2.5 Pro vs Claude 4:2025年高考物理真题实战对比评测(国内直接使用)
  • 【Java高频面试问题】JVM篇
  • python接口测试参数multipart/form-data格式不能有多余的空格或 tab 缩进
  • 逆向入门(8)汇编篇-rol指令的学习