自定义动态代理无法触发@Transactional,因其脱离Spring容器管理,事务切面未注册;应改用TransactionTemplate编程式事务或回归Spring标准事务机制。

自定义动态代理事务中的自调用失效,本质不是“代理没写好”,而是 Spring 声明式事务(@Transactional)本身依赖标准 AOP 代理机制,一旦你绕开 Spring 容器管理、手动构建代理(比如用 JDK Proxy 或 CGLIB 自己 new 代理对象),就脱离了 Spring 的事务上下文体系——此时 @Transactional 注解根本不会被解析,更谈不上生效。
为什么自定义代理无法触发 @Transactional
Spring 的事务切面(TransactionInterceptor)是在 Bean 初始化阶段由 TransactionAspectSupport 注册进代理链的,前提是:
- Bean 必须由 Spring 容器管理(@Service/@Component)
- 代理必须由 Spring 的 AbstractAutoProxyCreator(如 AnnotationAwareAspectJAutoProxyCreator)生成
- @Transactional 方法必须是 public,且调用路径必须经过该代理对象
你自己写的动态代理,哪怕逻辑一模一样,也不会自动加载 Spring 的事务拦截器。它只是一个普通代理,对注解无感知。
真正可行的替代方案(不依赖 @Transactional)
如果你确实需要在自定义代理中控制事务,就得放弃声明式事务,改用编程式事务 + 显式代理织入:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 用 TransactionTemplate 手动开启/提交/回滚:在代理的 invoke() 方法中,在目标方法执行前后调用 transactionTemplate.execute(status -> { ... })
- 把事务逻辑下沉到目标方法内部:让业务方法自己调用 TransactionTemplate,代理只负责转发,不参与事务决策
- 结合 AopContext.currentProxy():仅适用于 Spring 原生代理场景,不适用于你手写的代理;若坚持用自定义代理,此方式无效
更推荐的做法:回归 Spring 标准机制
绝大多数情况下,“自定义动态代理事务”属于过度设计。Spring 已提供成熟方案应对各类事务需求:
- 跨服务调用 → 使用分布式事务框架(Seata、Atomikos)
- 非 public 方法需事务 → 改为 public + 拆分到独立 @Service
- 需要灵活传播行为 → 直接配置 @Transactional(propagation = ...)
- 想绕过代理限制 → 启用 @EnableAspectJAutoProxy(exposeProxy = true),然后在代码中写 ((YourService) AopContext.currentProxy()).methodB()
关键提醒:避免循环依赖陷阱
如果选择注入自身 Bean 来解决自调用(如 @Autowired private YourService self),务必注意:
- Spring 默认单例 + 构造注入时可能引发 early bean exception
- 建议改用 @Lazy 注解或 ObjectProvider<YourService> 延迟获取
- 更稳妥的方式仍是拆分逻辑到不同 Service 类,职责清晰且无耦合风险

















