spring中service调用service的事务控制

本文探讨了在Spring框架中使用事务控制时可能出现的问题,特别是在一个Service调用另一个Service时的情况。文章通过实例说明了不同类型的异常如何影响事务的提交或回滚,并介绍了如何通过配置来解决这些问题。

    在使用SPRING的事务控制时,事务一般都是加在SERVICE层的,这个时候如果一个SERVICE调用另一个

SERVICE时有可能会出现事务控制问题,比如第二个SERVICE抛出了异常,第一个SERVICE却正常提交了,

比如下面这个例子:

事务配置

<tx:method name="add*" propagation="REQUIRED"/>

测试代码

    @Test
    public void testAddUser() throws Exception {
        UserServiceImpl service = (UserServiceImpl) context.getBean("userServiceImpl", UserServiceImpl.class);
        USER u = new USER();
        u.setCreated("2015-05-05");
        u.setCreator(123);
        u.setName("test");
        u.setPassword("test");
        service.addUser(u);
    }

SERVICE层:
    public void addUser(USER user) throws Exception {
        userDaoImpl.saveUser(user);
        delByUsername(user.getName());
    }

    public void delByUsername(String name) throws Exception {
        throw new FileNotFoundException("fjkdl");
    }
    当第二个SERVICE抛出异常的时候,第一个SERVICE却正常提交了,USER被加入到了数据库当中。

    先不改配置,我们改下SERVICE代码:

    public void addUser(USER user) throws Exception {
        userDaoImpl.saveUser(user);
        delByUsername(user.getName());
    }

    public void delByUsername(String name) throws Exception {
//        String s = null;
//        s.length();
        throw new RuntimeException("runtime e");
    }

    让第二个SERVICE抛出运行时异常,再测试,会发现这个时候第一个SERVICE的事务也回滚了,USER

没有插入数据库中。

    从测试中我们可以发现,在事务传播为propagation="REQUIRED"的时候,如果SERVICE中抛出检查型

异常,其它的事务可以正常提交,但是如果SERVICE抛出运行时异常,则所有的SERVICE共享同一事务。

    如果我们改下配置,如下:

<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
     这个时候,无论SERVICE里抛出运行时异常还是检查型异常,将共享同一事务,也就是说只要有异常,事务

将自动回滚。

    现在只发现了这个问题,如果大家遇到什么问题也可以提出来一起讨论下。

   


<think>我们正在讨论SpringService调用自身方法导致事务失效的问题。根据之前的对话,用户已经了解了问题的原因和两种解决方案(将方法移到另一个Service类或注入自身代理)。现在用户再次询问解决方法,可能是希望得到更详细的指导或更多方案。 因此,我会重申核心原因,并详细展开解决方案,同时补充其他可能的方案(如AspectJ)和最佳实践。 注意:根据系统指令,我们需要使用中文,正确使用LaTeX格式(虽然本问题可能不需要数学公式,但如果有公式则需遵守),并在回答末尾生成相关问题。 解决方案回顾与扩展: 1. 原因:Spring事务基于代理,同类内部调用不经过代理,导致事务失效。 2. 解决方案: a) 方法抽取到另一个Service(推荐) b) 自注入(注入自身代理) c) 使用AspectJ(编译时或加载时织入,无需代理) 3. 详细说明自注入的注意事项(避免循环依赖,使用setter注入或构造函数注入,但Spring通常能处理自注入的循环依赖) 4. 补充AspectJ方案(需要配置,但更强大,能解决内部调用问题,但增加了复杂度) 5. 最佳实践:推荐先考虑方法抽取,如果内聚性要求高则用自注入,对性能要求极高且项目允许则用AspectJ。 生成相关问题:围绕事务失效的其他原因、自调用问题的避免、AspectJ方案细节、事务传播行为的影响、测试方法等。 </think> ### Spring Service调用自身方法事务失效问题解决方案 #### 原因分析 Spring事务基于动态代理实现(AOP机制)。当Service类中**方法A调用自身方法B**(被`@Transactional`注解)时,实际调用路径为: $$ \text{方法A} \xrightarrow{\text{this.methodB()}} \text{方法B} $$ 此时调用通过**目标对象本身**(`this`)而非代理对象,导致事务拦截器无法生效[^1][^2]。 #### 三种可靠解决方案 --- ##### ✅ 方案1:方法拆分到不同Service(推荐) 将事务方法移至另一个Service类,通过依赖注入调用: ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; // 注入其他Service public void methodA() { serviceB.methodB(); // 通过代理对象调用事务生效 } } @Service public class ServiceB { @Transactional // 事务注解生效 public void methodB() { // 数据库操作 } } ``` **优点**:符合Spring设计规范,无循环依赖风险。 **缺点**:需拆分代码,适合方法逻辑独立场景[^1]。 ##### ✅ 方案2:注入自身代理实例 在Service中注入自己的代理对象,通过代理调用方法: ```java @Service public class UserService { @Autowired private UserService self; // 关键:注入自身代理 public void updateUser() { self.updateProfile(); // 通过代理调用 } @Transactional public void updateProfile() { // 事务操作 } } ``` **注意**: 1. 需开启代理支持(Spring Boot默认支持) 2. 避免循环依赖(可通过`@Lazy`解决)[^2]。 ##### ✅ 方案3:使用AspectJ模式(高级方案) 通过编译期/加载期织入取代动态代理: ```xml <!-- pom.xml 配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 启动类添加注解: ```java @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) // 启用AspectJ @SpringBootApplication public class App { ... } ``` **优势**: - 解决所有自调用问题 - 无代理开销 **代价**: - 需额外配置编译插件 - 增加应用启动时间。 #### 最佳实践 1. **优先方案1**:代码结构清晰,避免代理陷阱 2. **事务验证**:通过抛出异常测试回滚 ```java @Test void testTransaction() { assertThrows(RollbackException.class, () -> service.methodA()); } ``` 3. **传播行为**:检查`@Transactional(propagation = Propagation.REQUIRED)`等配置
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值