Spring项目中Service调用同类方法调用事务失效问题和处理方法

在日常开发中,有时候业务需求的原因,需要在Service层中本类的test()调用本类test2()方法,这时候test2()如果执行中发生异常,test()不会回滚,演示代码如下:

@ComponentScan("com.zlm")
@EnableAspectJAutoProxy
@EnableTransactionManagement
@Configuration
public class AppConfig {

    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager transactionManager(){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;

    }
}
@Component
public class UserService{

    @Autowired
    private OrderService orderService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(rollbackFor = Exception.class)
    public void test(){
        jdbcTemplate.execute("insert into user values(12,'zs')");
        test2();
    }

    //Propagation.NEVER表示调用该方法的调用方如果有事务就抛出异常
    @Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
    public void test2(){
        System.out.println("test2()");;
    }

}
public class Test {
    public static void main(String[] args) {
        String str = "UserService";
        System.out.println(Introspector.decapitalize(str));
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.test();

    }
}

我们会发现上面代码执行完后代码并没有报错,数据库中数据也插入了,并且也执行了test2().

分析问题原因:

spring事务控制是通过AOP来控制的,AOP又是使用动态代理来实现的,在main()中获取到的userService对象是代理对象,在执行test()是会先进入代理类的test()方法,在代理类test()中会对@Transactional做处理,然后才会执行真正的userService.test(),这时候执行到test2()时调用方已经不是代理类而是userService本身,所以不会对test2()上的注解做处理.

通过上面分析可以得出,只要让调用test2()时的调用对象是代理对象即可,解决方法:

1. 将test()和test2()分成两个类,调用test2()是注入test2()所在类的代理对象调用.

2. 在UserService中注入自己,调用test2()是使用注入的对象调用.

你可能感兴趣的:(java,spring,java)