spring中使用this调用事务失效的原因

本文探讨了在Spring框架中,当一个事务方法内部调用另一个带有REQUIRES_NEW传播属性的事务方法时,若采用this方式进行调用,则会导致事务失效的问题。文章通过示例代码详细解析了该现象,并给出了正确的解决方案。

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

  • spring中使用this调用事务失效的原因

spring中在一个拥有事务的方法A中调用另一个会挂起事务并创建新事务的方法B,如果使用this调用这个方法B,
此时方法B抛出了一个一场,此时的方法B的事务会失效的。并不会回滚。
如下:

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration(locations = { "classpath:Application-spring.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest extends AbstractJUnit4SpringContextTests {
}
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class TaskServiceImpl extends SpringTest {

    @Autowired
    public TaskService taskService ;

    @Test
    public void testPing() {
        taskService. test1();
    }

}
@Service
public class TaskService {

    @Autowired
    private TaskManageDAO taskManageDAO;

    @Transactional
    public void test1(){
        try {        
            this.test2();//这里调用会使事务失效,两条数据都会被保存
            /*           
            原因是:JDK的动态代理。
            在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象
            只有被动态代理直接调用的才会产生事务。
            这里的this是(TaskService)真实对象而不是代理对象
             */
             //解决方法
            TaskService proxy =(TaskService) AopContext.currentProxy();
            proxy.test2();
        }catch (Exception e){
            e.printStackTrace();
        }
        Task task = new Task();
        task.setCompleteBy("wjl练习1");
        task.setCompleteTime(new Date());
        taskManageDAO.save(task);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    // 这个事务的意思是如果前面方法有事务存在,会将前面事务挂起,再重启一个新事务
    public void test2(){
        Task task = new Task();
        task.setCompleteBy("wjl练习2");
        task.setCompleteTime(new Date());
        taskManageDAO.save(task);
        throw new RuntimeException();
    }
### Spring同一种类内部方法调用事务失效原因分析 在Spring框架中,声明式事务是基于AOP(面向切面编程)实现的。具体来说,当在一个方法上添加`@Transactional`注解时,Spring会通过动态代理的方式对该方法进行增强,从而实现了事务管理功能。然而,在同一类内的方法调用场景下,由于Java语言本身的特性以及Spring AOP的工作原理,可能导致事务失效。 #### 原因解析 在同一类内,假设存在两个方法:一个是带有`@Transactional`注解的方法(称为事务方法),另一个是没有事务注解的方法(称为非事务方法)。如果非事务方法直接调用事务方法,则该调用实际上是对当前对象自身的调用(即`this.method()`的形式)。这种情况下,调用的目标仍然是原始的对象而非经过Spring代理后的对象[^1]。因此,事务拦截器无法介入此次调用过程,导致事务失效。 --- ### 解决方案详解 以下是几种常见的解决方案及其适用场景: #### 方法一:将事务方法抽取至独立的服务类 可以通过重构代码结构,将原本位于同一个服务类中的事务方法移动到一个新的服务类中。这样做的好处在于强制使事务方法通过外部接口被调用,从而确保每次调用都经过Spring代理层,进而激活事务支持[^2]。 示例代码如下: ```java @Service public class MyService { @Autowired private AnotherService anotherService; public void nonTransactionMethod() { // 调用AnotherService中的事务方法 anotherService.transactionalMethod(); } } @Service public class AnotherService { @Transactional public void transactionalMethod() { // 此处为事务逻辑 } } ``` --- #### 方法二:利用`ApplicationContext`获取代理对象并调用目标方法 另一种方式是在需要调用事务方法的地方,借助Spring容器提供的上下文工具类`ApplicationContext`来获取当前Bean的代理实例,并通过此代理实例完成对事务方法的调用。这种方法虽然有效,但在实际开发中可能显得不够优雅[^3]。 示例代码如下: ```java @Autowired private ApplicationContext applicationContext; public void nonTransactionMethod() { MyService proxyInstance = (MyService) applicationContext.getBean(MyService.class); proxyInstance.transactionalMethod(); // 使用代理对象调用事务方法 } @Transactional public void transactionalMethod() { // 此处为事务逻辑 } ``` --- #### 方法三:启用代理对象暴露机制并通过`AopContext.currentProxy()`访问代理对象 为了简化上述第二种方法的操作流程,可以在应用启动配置阶段开启代理对象暴露选项(即设置`@EnableAspectJAutoProxy(exposeProxy=true)`属性)。之后即可通过静态方法`AopContext.currentProxy()`快速获得当前线程绑定的代理对象[^5]。 示例代码如下: ```java @EnableAspectJAutoProxy(exposeProxy = true) @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } @Service public class MyService { @Autowired private EmployeeRepository employeeRepository; public void nonTransactionMethod() { ((MyService) AopContext.currentProxy()).transactionalMethod(); } @Transactional public void transactionalMethod() { // 执行数据库操作... } } ``` 需要注意的是,使用这种方式的前提条件之一就是必须显式指定允许暴露代理对象;否则运行期间将会抛出异常提示未找到有效的代理实例。 --- #### 方法四:调整设计模式避免同类间直接调用 最后还可以考虑从业务层面重新审视现有架构是否存在过度耦合的情况。比如尝试引入事件驱动模型或者命令查询职责分离(CQRS)原则等方式减少不必要的跨层次交互频率,从根本上规避掉此类问题的发生可能性[^4]。 --- ### 总结 综上所述,对于Spring框架下的相同类别成员函数之间相互调用引发的事务丢失现象,主要源于其底层依赖于CGLIB字节码生成技术构建而成的代理机制所致。针对这一情况提供了多种可行的技术手段予以应对,开发者可以根据项目的具体情况灵活选用最合适的策略加以实施。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值