@Transactional注解失效,基于注解的切面失效问题

文章讲述了在Spring框架中,由于动态代理机制,当在方法内部直接调用其他有@Transactional注解的方法时,事务可能不会生效。作者提供了通过AopContext.currentProxy获取代理对象并调用方法以解决此问题的方法,并强调了在启动类中开启exposeProxy属性的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:轮到我了哈哈,最近在测接口的时候发现@Transactional标记的方法事务没有生效,问了同事是说与动态代理有关,于是我复习了一下,顺便做个记录

问题重现

如果你已经完全理解动态代理,那么我讲一句话你应该就明白为什么失效了(可以跳过此部分)

即:在方法内部自己调用方法

public class XxxxServiceImpl extends ServiceImpl<XxxxMapper, Xxxx> implements XxxxService{
    
    @Override
    @Transactional
    public void funcA(){
        // 代码略
    }
    
    @Override
    @Transactional
    public void funcB(){
        
        this.funcA() // 问题代码
    }
}

上述代码中,在调用XxxxService.funcB()时,内部的funcA()方法的事务会失效。

同理,如果将@Transactional换成自定义的注解,然后使用自定义的切面去扫描,打断点测试只会扫描到funcB(),而同样有注解的funcA()则不会触发断点。

如何修复

其实只要复习一下动态代理的过程就能明白,无论是spring实现的事务还是自己实现基于注解的切面逻辑,都是通过代理对象调用方法来实现对方法的增强

所以目标很明确了,我们在类内部调用自己的方法时,如果需要触发动态代理的增强逻辑,则应该通过代理对象调用自己内部的方法

如何获取当前对象的代理对象?

AopContext.currentProxy()// 这行代码就可以获取当前对象的代理对象

所以修改后如下,此时两个事务都能触发

public class XxxxServiceImpl extends ServiceImpl<XxxxMapper, Xxxx> implements XxxxService{
    
    @Override
    @Transactional
    public void funcA(){
        // 代码略
    }
    
    @Override
    @Transactional
    public void funcB(){
        // 通过代理对象调用内部方法
        XxxxServiceImpl proxy = (XxxxServiceImpl) AopContext.currentProxy()
        proxy.funcA() 
    }
    
}

别忘记去启动类开启暴露代理对象

@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)
@SpringBootApplication
@EnableTransactionManagement
@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true) // 添加这行代码
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}
### Spring Boot 多数据源环境下 `@Transactional` 注解失效原因分析 在多数据源环境中,`@Transactional` 注解可能失效的主要原因是事务管理器未能正确绑定到目标数据源。Spring 使用 AOP(面向切面编程)机制来处理 `@Transactional` 注解的方法,在运行时为带有该注解的 Bean 创建代理对象[^2]。然而,在多数据源场景下,默认的事务管理器通常只针对单个数据源配置。 当多个数据源存在时,如果未显式指定哪个事务管理器应与特定的数据源关联,则可能导致事务无法正常启动或回滚。此外,动态切换数据源的注解(如 `@DS`),如果没有正确应用到服务层实现类或其实现的具体方法上,也可能导致事务失效[^3]。 另外一种常见情况是异常被捕获而未重新抛出,这会阻止 `@Transactional` 注解检测到异常的发生,从而使得事务不执行回滚操作[^4]。 --- ### 解决方案 #### 1. 配置多个事务管理器 为了支持多数据源环境下的事务管理,需分别为每个数据源定义独立的事务管理器,并通过自定义注解将其绑定至对应的服务逻辑中: ```java @Configuration public class DataSourceConfig { @Bean(name = "dataSourceOneTransactionManager") public PlatformTransactionManager dataSourceOneTransactionManager(@Qualifier("dataSourceOne") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "dataSourceTwoTransactionManager") public PlatformTransactionManager dataSourceTwoTransactionManager(@Qualifier("dataSourceTwo") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } ``` 接着可以创建一个基于 `@Transactional` 的扩展注解用于区分不同的事务管理器: ```java @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional(value = "dataSourceOneTransactionManager") public @interface TransactionalDataSourceOne {} @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional(value = "dataSourceTwoTransactionManager") public @interface TransactionalDataSourceTwo {} ``` 这样可以在业务代码中按需选择合适的事务管理器: ```java @Service public class MyService { @TransactionalDataSourceOne public void methodForDataSourceOne() { // 数据库操作... } @TransactionalDataSourceTwo public void methodForDataSourceTwo() { // 另一数据库操作... } } ``` --- #### 2. 正确使用动态数据源切换注解 对于动态数据源切换的需求,推荐将 `@DS` 或类似的注解放置于 **服务实现类** 中而非接口处。这是因为 Spring AOP 默认采用 JDK 动态代理方式工作,只有实现了接口的对象才会被代理;而对于没有接口的情况则需要启用 CGLIB 支持。 可以通过如下设置开启 CGLIB 模式: ```properties spring.aop.proxy-target-class=true ``` 同时确保 `@DS` 被放置于实际执行业务逻辑的位置,例如: ```java @Service public class UserServiceImpl implements UserService { @Override @DS("slave") public User getUserById(Long id) { // 查询用户信息... } } ``` --- #### 3. 确保异常传播行为满足需求 为了避免因异常捕获而导致事务失效的现象发生,应当注意不要在受控方法内部吞掉任何已知错误。即使某些情况下确实希望局部处理部分问题,也建议手动重投这些异常以便触发必要的回滚动作。 例如下面的例子展示了如何让 checked exception 强制引发 rollback: ```java @AfterThrowing(pointcut="execution(* com.example.service.*(..))", throwing="ex") public void handleException(Exception ex){ throw new RuntimeException(ex); } ``` --- ### 总结 综上所述,解决 Spring Boot 多数据源环境下 `@Transactional` 注解失效的关键在于合理配置多个事务管理器并明确其作用范围,同时保证动态数据源切换策略的有效实施以及适当调整程序结构以适应框架的工作原理。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fan_11235813

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值