@Transactional类内部访问失效原因详解
一、原理
Spring之所以可以对开启@Transactional的方法进行事务管理,是因为Spring为当前类生成了一个代理类,然后在执行相关方法时,会判断这个方法有没有@Transactional注解,如果有的话,则会开启一个事务。
也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。(该部分摘自:事务注解Transactional在同一个类中调用的失效问题_fastjson_的博客-CSDN博客)
Spring在扫描bean的时候,如果扫描到方法上有这些注解,那么spring会通过动态代理模式,为这个bean动态地生成一个代理类,在代理类中,会对有注解的这个方法,做一些增强处理,如给有@Transactional注解的方法开启transaction。
当我们想要调用这个方法时,实际上是先调用了代理对象中被增强的方法,然后在代理对象中,又会调用我们实际的目标对象中的方法。在通过代理对象中转的这一过程中,像上边说的开启和提交transaction就实现了。(该部分摘自:@transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!…_weixin_39738667的博客-CSDN博客)
二、代码模拟
实现机制我们大体知道了,下面我们用代码来模拟演示一下。
假设我们订单业务处理类中,有两个方法:校验订单参数方法verifyOrderParameters() 和 保存订单方法saveOrder(),其中saveOrder方法上加了@Transactional注解,verifyOrderParameters方法内会调用saveOrder方法。
OrderService
/**
* 订单业务层的接口定义
*/
public interface OrderService {
/**
* 校验订单参数
*/
void verifyOrderParameters();
/**
* 保存订单
*/
void saveOrder();
}
OrderServiceImpl
/**
* 订单业务层的具体处理类
*/
public class OrderServiceImpl implements OrderService{
@Override
public void verifyOrderParameters() {
System.out.println("校验订单参数");
// 调用保存订单方法
saveOrder();
}
@Override
@Transactional
public void saveOrder() {
System.out.println("保存订单信息到DB");
}
}
我们用伪代码来演示一下Spring动态代理生成的代理类。
OrderServiceImplProxy
/**
* 订单业务层具体处理类的代理类
*/
public class OrderServiceImplProxy implements OrderService{
/**
* 持有被代理的具体的目标对象
*/
private OrderServiceImpl orderServiceImpl;
public OrderServiceImplProxy(OrderServiceImpl orderServiceImpl) {
this.orderServiceImpl = orderServiceImpl;
}
@Override
public void verifyOrderParameters() {
orderServiceImpl.verifyOrderParameters();
}
@Override
public void saveOrder() {
System.out.println("开启事务。。。");
orderServiceImpl.saveOrder();
System.out.println("提交事务。。。");
}
}
这边我们来看下客户端调用saveOrder方法和verifyOrderParameters方法,输出结果都是什么样的。
public class Client {
public static void main(String[] args) {
// 创建一个订单业务的真实处理对象
OrderServiceImpl orderServiceImpl = new OrderServiceImpl();
// 创建一个代理对象
OrderServiceImplProxy orderServiceImplProxy = new OrderServiceImplProxy(orderServiceImpl);
// 执行代理对象的校验订单方法
orderServiceImplProxy.verifyOrderParameters();
System.out.println("--------------------------------------------");
// 执行代理对象的保存订单方法
orderServiceImplProxy.saveOrder();
}
}
执行main方法后,得到下边的输出:
校验订单参数
保存订单信息到DB
--------------------------------------------
开启事务。。。
保存订单信息到DB
提交事务。。。
所以,普通方法verifyOrderParameters内调用注解方法saveOrder时,其实调用的是原目标对象(orderServiceImpl)的saveOrder方法,没有走代理对象(orderServiceImplProxy)中被增强的saveOrder方法,所以就不会产生效果啦。
该部分摘自:@transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!…_weixin_39738667的博客-CSDN博客
三、解决方案
方法1:
将事务方法放到另一个类中进行调用。
方法2:
获取本对象的代理对象,再进行调用。具体操作如:
@Service
public class OrderService {
private void insert() {
insertOrder();
}
@Transactional
public void insertOrder() {
//SQL操作
}
}
变更为:
@Service
public class OrderService {
public void insert() {
OrderService proxy = (OrderService) AopContext.currentProxy();
proxy.insertOrder();
}
@Transactional
public void insertOrder() {
//SQL操作
}
}
最后
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
小编已加密:aHR0cHM6Ly9kb2NzLnFxLmNvbS9kb2MvRFVrVm9aSGxQZUVsTlkwUnc==出于安全原因,我们把网站通过base64编码了,大家可以通过base64解码把网址获取下来。