springboot 调用本service 的方法 事务方法

本文介绍了一个Spring框架下的服务实现类AServiceImpl1,该类通过@Autowired注解进行自我注入,并使用@Transactional注解来管理两个方法a()和b()的事务传播行为。方法a()调用了方法b(),展示了不同事务传播级别的应用。

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

在Spring Boot应用中,调用同一个服务层(Service)中的事务方法时,需要注意如何正确地管理和传播事务边界。Spring的事务管理是基于AOP(面向切面编程)的,因此在调用同一个服务层中的方法时,如果没有特别的配置,事务边界可能会出现问题。以下是几种常见的处理方式:

同一对象内部调用

在同一服务对象内部直接调用方法时,事务不会被传播。这是因为方法调用是在同一对象内部完成的,没有触发AOP拦截。

示例

@Service
public class UserService {

    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
        deleteUserRoles(user.getId());
    }

    private void deleteUserRoles(Long userId) {
        roleRepository.deleteByUserId(userId);
    }
}

在这个例子中,deleteUserRoles 方法是在同一个事务中执行的,但如果没有显式地管理事务边界,deleteUserRoles 方法的事务性操作可能不会生效。

跨对象调用

如果在服务层内部调用另一个服务层的方法,则可以通过Spring的代理机制来传播事务。

示例

@Service
public class UserService {

    private final RoleService roleService;

    public UserService(RoleService roleService) {
        this.roleService = roleService;
    }

    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
        roleService.deleteUserRoles(user.getId());
    }
}

@Service
public class RoleService {

    private final RoleRepository roleRepository;

    public RoleService(RoleRepository roleRepository) {
        this.roleRepository = roleRepository;
    }

    @Transactional
    public void deleteUserRoles(Long userId) {
        roleRepository.deleteByUserId(userId);
    }
}

在这个例子中,通过依赖注入的方式,UserService 调用了 RoleService 中的方法,事务可以正确地传播。

显式调用代理对象的方法

即使在同一服务对象内部,也可以通过获取代理对象来显式地调用方法,从而触发事务管理。

示例

@Service
public class UserService {

    @Autowired
    private UserService userServiceProxy; // 自动注入代理对象

    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
        ((UserService)userServiceProxy).deleteUserRoles(user.getId()); // 显式调用代理对象的方法
    }

    @Transactional
    public void deleteUserRoles(Long userId) {
        roleRepository.deleteByUserId(userId);
    }
}

这种方法通过Spring容器提供的代理对象来调用自身的方法,从而保证事务能够正确传播。

使用@Async异步调用

如果方法需要异步执行,可以使用@Async注解来调用方法,这种方式同样会触发事务传播。

示例

@Service
public class UserService {

    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
        deleteUserRolesAsync(user.getId());
    }

    @Async
    @Transactional
    public void deleteUserRolesAsync(Long userId) {
        roleRepository.deleteByUserId(userId);
    }
}

使用@Async注解可以确保方法在异步调用时依然受到事务管理的影响。

Spring的事务管理

Spring的事务管理是Spring框架的核心特性之一,它使得开发者能够在编写应用程序时更加容易地管理事务。Spring提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。以下是这两种方式的基本介绍及其实现方法:

编程式事务管理

编程式事务管理是指在代码中显式地管理事务的开始提交回滚。这种方式适用于需要细粒度控制事务的情况。

示例代码
@Autowired
private PlatformTransactionManager transactionManager;

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

public void updateUser(User user) {
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

    try {
        userRepository.save(user);
        roleRepository.deleteByUserId(user.getId());
        transactionManager.commit(status);
    } catch (Exception e) {
        transactionManager.rollback(status);
        throw e;
    }
}

声明式事务管理

声明式事务管理是通过使用@Transactional注解来标记需要事务管理的方法,这种方式更为简洁,易于维护。

示例代码
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
        roleRepository.deleteByUserId(user.getId());
    }
}

事务配置

为了使Spring能够管理事务,你需要在Spring配置中定义事务管理器(PlatformTransactionManager)。

使用XML配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>
使用Java配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

@Transactional注解的属性

@Transactional注解提供了许多属性来定制事务的行为:

属性描述可选值/默认值
propagation指定事务的传播行为。- REQUIRED(默认):如果当前存在事务,则加入该事务;否则新建一个事务。
- SUPPORTS:如果当前存在事务,则加入该事务;否则以非事务的方式继续执行。
- MANDATORY:如果当前存在事务,则加入该事务;否则抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,则挂起当前事务。
- NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- NOT_SUPPORTED:以非事务方式运行,并挂起当前事务(如果有)。
- NESTED:如果当前存在事务,则新建一个嵌套事务;否则效果等同于 REQUIRED
isolation指定事务的隔离级别。- DEFAULT(默认):使用底层数据库的默认隔离级别。
- READ_UNCOMMITTED:未提交读。
- READ_COMMITTED:已提交读。
- REPEATABLE_READ:可重复读。
- SERIALIZABLE:序列化。
readOnly指定事务是否为只读事务。false(默认),可以设置为 true
timeout指定事务超时时间(秒),超过此时间后将回滚事务。默认无限制。
rollbackFor指定哪些异常会导致事务回滚。默认为 RuntimeException.class
noRollbackFor指定哪些异常不会导致事务回滚。默认无。

总结

Spring Boot应用中,确保事务正确传播的关键在于理解Spring AOP的工作原理。通过上述方法之一,可以确保在服务层内部调用事务方法时,事务能够按照预期的方式管理。如果仍然遇到问题,可以检查Spring的事务配置是否正确,以及方法上的@Transactional注解是否遗漏。

<think>嗯,用户问的是在Spring中,当Service调用自己的方法事务不生效的解决方法。这个问题我之前有遇到过,可能得回忆一下Spring事务的工作原理。 首先,Spring事务管理是基于AOP的代理机制,也就是说,只有通过代理对象调用方法事务才会生效。如果一个Service类中的方法A直接调用了同一个类中的方法B,而方法B上有@Transactional注解,这时候事务可能不会生效。这是因为方法A调用方法B的时候,实际上是通过this对象调用的,而不是通过代理对象,所以AOP切面无法介入,事务就不会被应用。 那解决办法的话,可能需要让调用通过代理对象来进行。常见的解决方案有几个: 第一种是让方法A和方法B在不同的类中,这样通过Spring的代理机制,事务就能正常生效。不过这可能涉及到代码结构的调整,把方法B移到另一个Service里,这样调用的时候就会走代理了。 第二种办法是从Spring容器中获取代理后的自身实例。比如在Service中注入自己的代理对象,然后通过这个代理对象来调用方法B。不过需要注意的是,这样可能会引起循环依赖的问题,需要确保Spring的配置允许这种情况。 第三种方法是在配置类中添加@EnableAspectJAutoProxy(exposeProxy = true),然后在代码中使用AopContext.currentProxy()来获取当前代理对象,再调用方法。不过这样做的话,需要确保代码运行时有权限访问AopContext,可能需要配置CGLIB代理。 另外,可能还要检查事务的配置是否正确,比如是否启用了事务管理,注解是否放在了接口或者实现类上,以及传播行为是否设置正确。例如,方法B的事务传播行为可能需要设置为REQUIRES_NEW或者其他合适的级别,但这也取决于具体的业务需求。 还可能要考虑使用的代理方式是JDK动态代理还是CGLIB。如果Service类没有实现接口,默认情况下Spring会使用CGLIB,这时候自调用的问题可能通过上述方法解决。但如果使用的是JDK动态代理,可能更需要注意这一点。 总结一下,解决自调用事务不生效的问题,核心在于确保方法调用经过代理对象,从而触发事务拦截器。根据不同的情况选择不同的方法,比如拆分类、获取代理实例或调整配置。同时,检查事务相关的配置是否正确,排除其他可能导致事务失效的因素。</think>### Service调用自身方法事务不生效的解决方法 #### 问题原因 Spring事务管理基于代理机制(JDK动态代理或CGLIB),当通过`this`调用同一个类中的方法时,会绕过代理对象直接调用目标方法,导致`@Transactional`注解失效。 --- #### 解决方案及步骤 ##### 方法1:拆分方法到不同类中 **核心思路**:将需要事务管理的方法拆分到独立的Service类中,通过依赖注入调用。 **步骤**: 1. 新建`ServiceB`类,将原`ServiceA`中需要事务管理的方法移动到`ServiceB`中。 2. 在`ServiceA`中注入`ServiceB`。 3. 通过`ServiceB`调用方法。 ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; public void methodA() { // 业务逻辑 serviceB.methodB(); // 通过代理对象调用 } } @Service public class ServiceB { @Transactional public void methodB() { // 需要事务的业务逻辑 } } ``` --- ##### 方法2:注入自身代理对象 **核心思路**:通过Spring容器获取代理后的自身实例,用代理对象调用方法。 **步骤**: 1. 在`Service`类中注入自身代理对象。 2. 通过代理对象调用方法。 ```java @Service public class MyService { @Autowired private MyService selfProxy; // 注入自身代理对象 public void methodA() { // 业务逻辑 selfProxy.methodB(); // 通过代理对象调用 } @Transactional public void methodB() { // 需要事务的业务逻辑 } } ``` **注意**:需在配置类中添加`@EnableAspectJAutoProxy(exposeProxy = true)`。 --- ##### 方法3:使用AopContext获取当前代理 **核心思路**:通过`AopContext.currentProxy()`获取当前代理对象。 **步骤**: 1. 配置类启用`exposeProxy`。 2. 在代码中显式获取代理对象。 ```java @SpringBootApplication @EnableTransactionManagement @EnableAspectJAutoProxy(exposeProxy = true) public class AppConfig { } @Service public class MyService { public void methodA() { MyService proxy = (MyService) AopContext.currentProxy(); proxy.methodB(); // 通过代理对象调用 } @Transactional public void methodB() { // 需要事务的业务逻辑 } } ``` **条件**:需使用CGLIB代理(在配置中设置`proxyTargetClass=true`)。 --- #### 其他注意事项 1. **检查事务配置** - 确保配置类添加了`@EnableTransactionManagement`。 - 检查`@Transactional`注解是否在`public`方法上。 2. **事务传播行为** 若需要新事务,可设置传播行为为`Propagation.REQUIRES_NEW`: ```java @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { ... } ``` 3. **代理模式选择** 如果类没有实现接口,强制使用CGLIB代理: ```properties spring.aop.proxy-target-class=true ``` --- #### 总结 | 方法 | 优点 | 缺点 | |---------------------|-----------------------|------------------------------| | 拆分到不同类 | 代码清晰,符合设计规范 | 需改动代码结构 | | 注入自身代理对象 | 改动小 | 需处理循环依赖 | | 使用AopContext | 灵活性高 | 需配置`exposeProxy`和CGLIB |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Yweir

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

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

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

打赏作者

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

抵扣说明:

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

余额充值