这点比较有意思,这里特别说明下,这里先举个例子说明这种情况:
java 代码解读复制代码//保存父方法
public void saveParentMethod() {
//插入parent
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("parent");
this.stockInfoMapper.insertSelective(stockInfo);
//插入child
this.saveChildStockInfo();
}
//保存子方法
@Transactional(rollbackFor = Exception.class)
public void saveChildStockInfo() {
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("child");
this.stockInfoMapper.insertSelective(stockInfo);
//制造异常
int i = 1 / 0;
}
这里首先可以看到saveParentMethod方法是没有事务的;而saveChildStockInfo却是有事务的。当测试类调用
saveParentMethod方法后,你会发现事务完全不起作用了。可能在我们的理解中:parent应该入库而child不应该
入库。然而实际情况是:child也入库了,明显是事务失效了。
spring基于AOP机制实现事务的管理。spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,若是包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,其实是由代理类来调用的,代理类在调用以前就会启动transaction。然而,若是这个有注解的方法是被同一个类中的其余方法调用的,那么该方法的调用并无经过代理类,而是直接经过原来的那个bean,因此就不会启动transaction,最后看到的现象就是@Transactional注解无效。
就拿开始的例子如果saveParentMethod上有@Transactional注解,自然就不会出现不起作用的情况了。
(感觉这个方法很没有营养的)
这个方法虽然也有点看着愚蠢,但是的确很多情况下很实用。尤其是如果你的servce叫XXService。那么分出的有
事务的方法可以放在XXServiceHelper类中。既能解决事务不起作用的问题,同样可以使你的主Service变的很清爽。所以这个方法,在某些情况下反而非常适合。
java 代码解读复制代码@Service
public class OuterBean {
@Resource
private StockInfoMapper stockInfoMapper;
//自己注入自己
@Resource
private OuterBean outerBean;
public void saveParentMethod() {
//插入parent
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("parent");
this.stockInfoMapper.insertSelective(stockInfo);
//插入child。这里相当于调用代理的方法
this.outerBean.saveChildStockInfo();
}
@Transactional(rollbackFor = Exception.class)
public void saveChildStockInfo() {
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("child");
this.stockInfoMapper.insertSelective(stockInfo);
//制造异常
int i = 1 / 0;
}
}
可能有人会担心这样会有循环依赖的问题,事实上,spring通过三级缓存解决了循环依赖的问题,所以上面的写法不会有循环依赖问题。但是!!!,这不代表spring永远没有循环依赖的问题(@Async导致循环依赖了解下)
既然我们知道@Transactional是通过aop来实现的,这里就很容易想到--只要拿到代理我们Servcie的那个对象就可以了。于是就有了如下代码:
java 代码解读复制代码@Service
public class OuterBean {
@Resource
private StockInfoMapper stockInfoMapper;
@Autowired
private ApplicationContext applicationContext;
public void saveParentMethod() {
//插入parent
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("parent");
this.stockInfoMapper.insertSelective(stockInfo);
//通过ApplicationContext获取代理对象
this.applicationContext.getBean(OuterBean.class).saveChildStockInfo();
}
@Transactional(rollbackFor = Exception.class)
public int saveChildStockInfo() {
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("child");
int insert = this.stockInfoMapper.insertSelective(stockInfo);
//制造异常
int i = 1 / 0;
return insert;
}
}
和上面的方式原理差不多,只是获得代理类的方式不同。代码如下:
java 代码解读复制代码#这里多加一个批注,不加会报错:Set 'exposeProxy' property on Advised to 'true' to make it available
@EnableAspectJAutoProxy(proxyTargetClass=true, exposeProxy=true)
@Service
public class OuterBean {
@Resource
private StockInfoMapper stockInfoMapper;
public void saveParentMethod() {
//插入parent
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("parent");
this.stockInfoMapper.insertSelective(stockInfo);
//通过AopContext调用saveChildStockInfo方法
((OuterBean) AopContext.currentProxy()).saveParentMethod();
}
@Transactional(rollbackFor = Exception.class)
public void saveChildStockInfo() {
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("child");
this.stockInfoMapper.insertSelective(stockInfo);
//制造异常
int i = 1 / 0;
}
}
此方法还是非常推荐的。使用简单而且不会有循环依赖的问题,非常的nice。
java 代码解读复制代码#新加一个TransactionHandler类
@Service
public class TransactionHandler {
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public <T> T runInTransaction(Supplier<T> supplier) {
return supplier.get();
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public <T> T runInNewTransaction(Supplier<T> supplier) {
return supplier.get();
}
}
#改造OuterBean
@Service
public class OuterBean {
@Resource
private StockInfoMapper stockInfoMapper;
@Autowired
private TransactionHandler transactionHandler;
public void saveParentMethod() {
//插入parent
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("parent");
this.stockInfoMapper.insertSelective(stockInfo);
//通过TransactionHandler调用saveChildStockInfo方法
this.transactionHandler.runInTransaction(() -> saveChildStockInfo());
}
public int saveChildStockInfo() {
StockInfo stockInfo = new StockInfo();
stockInfo.setProductId("child");
int insert = this.stockInfoMapper.insertSelective(stockInfo);
//制造异常
int i = 1 / 0;
return insert;
}
}
这个方法看着有点麻烦。但是有几个优势是其他方式无法比拟的:
spring的aop代理有jdk代理和cglib代理实现,通过如下代码来区分:
java 代码解读复制代码//是否代理对象
AopUtils.isAopProxy(AopContext.currentProxy());
//是否cglib 代理对象
AopUtils.isCglibProxy(AopContext.currentProxy());
//是否dk动态代理
AopUtils.isJdkDynamicProxy(AopContext.currentProxy());
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。