在日常开发中,有时候业务需求的原因,需要在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()是使用注入的对象调用.