在spring 中进行事物管理的时候,N种情况会出现事物回滚失败
1)在事物中调用自己的其他的方法,但是进行捕获了异常。
@Transtaion
public void save(){
try{
save2()
}catch{
}
mapper.insert()
}
@Transatinal(开启新的事物)
public void save2(){
mapper.insert()
1/0
}
这里的调用会出现回滚失败的问题。我们本来想要实现的功能是,第一个插入失败了,因为出现了异常。需要进行回滚
但是没有回滚,这一次两个记录都会插入成功。
这是因为Spring中的事物是通过Aop来实现的,代理类在进行调用save 方法的时候是通过代理调用的,此时会开启事物,但是这里如果是调用自身方法的时候,就是没有通过代理对象来调用,而是通过ServerImpl自身的方法,此时是没有开启事物的。因此不会进行回滚。
这里如果想要做到回滚的话,可以通过拿到我们的代理类来进行调用。
获取代理类的方法有多种,
1)
通过ThreadLocal暴露Aop代理对象
1、开启暴露Aop代理到ThreadLocal支持(如下配置方式从spring3开始支持)
- <aop:aspectj-autoproxy expose-proxy="true"/><!—注解风格支持-->
- <aop:config expose-proxy="true"><!—xml风格支持-->
2)ApplicationContext
一、定义BeanPostProcessor 需要使用的标识接口
- public interface BeanSelfAware {
- void setSelf(Object proxyBean);
- }
即我们自定义的BeanPostProcessor (InjectBeanSelfProcessor)如果发现我们的Bean是实现了该标识接口就调用setSelf注入代理对象。
二、Bean实现
- @Service
- public class AServiceImpl4 implements AService, BeanSelfAware {//此处省略接口定义
- private AService proxySelf;
- public void setSelf(Object proxyBean) { //通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
- this.proxySelf = (AService) proxyBean;
- }
- @Transactional(propagation = Propagation.REQUIRED)
- public void a() {
- proxySelf.b();//调用代理对象的方法 这样可以执行事务切面
- }
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- public void b() {
- }
- }
改进版的InjectBeanSelfProcessor的解决方案
- @Component
- public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {
- private ApplicationContext context;
- //① 注入ApplicationContext
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.context = applicationContext;
- }
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过
- return bean;
- }
- if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入
- ((BeanSelfAware) bean).setSelf(bean);
- } else {
- //④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入
- //此种方式不适合解决prototype Bean的代理对象注入
- ((BeanSelfAware)bean).setSelf(context.getBean(beanName));
- }
- return bean;
- }
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- return bean;
- }
- }
3)
通过初始化方法在目标对象中注入代理对象
- @Service
- public class AServiceImpl3 implements AService{
- @Autowired //① 注入上下文
- private ApplicationContext context;
- private AService proxySelf; //② 表示代理对象,不是目标对象
- @PostConstruct //③ 初始化方法
- private void setSelf() {
- //从上下文获取代理对象(如果通过proxtSelf=this是不对的,this是目标对象)
- //此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean
- proxySelf = context.getBean(AService.class);
- }
- @Transactional(propagation = Propagation.REQUIRED)
- public void a() {
- proxySelf.b(); //④ 调用代理对象的方法 这样可以执行事务切面
- }
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- public void b() {
- }
- }
@Transtaion
public void save(){
try{
(Service(AopContext.currentProxy())).save2()
}catch{
}
mapper.insert()
}
解决办法2: 如下或抛出RuntimeException- if(userSave){
- try {
- userDao.save(user);
- userCapabilityQuotaDao.save(capabilityQuota);
- } catch (Exception e) {
- logger.info("能力开通接口,开户异常,异常信息:"+e);
- TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
- }
- }
2)AOP配置文件没有正确配置的
3)Controller package 和service package 要精确配置到对应的包
参考:https://www.cnblogs.com/tianyuchen/p/6678084.html
https://www.cnblogs.com/hanxue53/p/5280099.html