在业务开发中,我们在一个类中一个服务方法调用同一个类服务方法,发现事务失效,代码如下:
比如一个订单入库:
public class OrderService implements IOrderService {
@Override
@Transactional
public Boolean initOrder() {
//初始主订单
return Boolean.TRUE;
}
private Boolean initProdcut(Long productId) {
//初始化订单对应得商品
return Boolean.TRUE;
}
}
方法initProduct事务不生效,出现场景如上。
首先通过分析事务不生效步骤如下:
1、检查存储引擎是否是innodb。
2、检查项目中事务是否生效,检查项目是否正确开启事务。
3、检查配置是否配置正确,主要检查:@Transactional 注解的 rollbackFor 捕获的异常范围小于代码抛出的异常,导致不会滚; 设置了事务的timeout时间,代码逻辑执行超时了,导致事务失效
;被@Transactional 注解修饰的方法, 修饰符非public 或者被final修饰. Aop没办法为其生成一个代理。
4、项目是否存在this调用。
通过分析以前属于第4点;那我通过JDK动态代理实现代码来分析为什么不会生效。通过新增用户的同时检查用户同时在打印调用方法日志,代码如下。
用户操作接口:
public interface IUserService {
/**
* 新增用户
* @return
*/
public Integer addUser();
/**
* 检查用户信息
* @return
*/
public Boolean checkUser();
}
操作用户实现:
public class UserService implements IUserService {
@Override
public Integer addUser() {
System.out.println("新增用户");
this.checkUser();
return 0;
}
@Override
public Boolean checkUser() {
System.out.println("检查用户");
return Boolean.TRUE;
}
}
日志处理器:
public class LogHanler implements InvocationHandler {
private Object object;
public LogHanler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(String.format("调用方法开始:[%s] ", method.getName()));
Object result = method.invoke(object, args); // 调用 target 的 method 方法
System.out.println(String.format("调用方法结束[%s] ", method.getName()));
return result;
}
}
运行:
public static void main(String[] args) {
UserService userService = new UserService();
LogHanler logHanler = new LogHanler(userService);
ClassLoader classLoader = userService.getClass().getClassLoader();
IUserService o = (IUserService)Proxy.newProxyInstance(classLoader, userService.getClass().getInterfaces(), logHanler);
o.addUser();
}
运行结果如下:
调用方法开始:[addUser]
新增用户
检查用户
调用方法结束[addUser]
细心的朋友会发现没有打印调用方法checkUser。我在调整一下代码,我把动态代码类传入到UserService 服务中。同时我在IUserService 接口中新增 setUserService ,代码调整如下:
用户操作接口:
public interface IUserService {
/**
* 新增用户
* @return
*/
public Integer addUser();
/**
* 检查用户信息
* @return
*/
public Boolean checkUser();
}
操作用户实现:
public class UserService implements IUserService {
private IUserService userService;
@Override
public Integer addUser() {
System.out.println("新增用户");
userService.checkUser();
return 0;
}
@Override
public Boolean checkUser() {
System.out.println("检查用户");
return Boolean.TRUE;
}
}
运行代码调整:
public static void main(String[] args) {
UserService userService = new UserService();
LogHanler logHanler = new LogHanler(userService);
ClassLoader classLoader = userService.getClass().getClassLoader();
IUserService o = (IUserService)Proxy.newProxyInstance(classLoader, userService.getClass().getInterfaces(), logHanler);
userService.setUserService(o);
o.addUser();
}
运行结果如下:
调用方法开始:[setUserService]
调用方法结束[setUserService]
调用方法开始:[addUser]
新增用户
调用方法开始:[checkUser]
检查用户
调用方法结束[checkUser]
调用方法结束[addUser]
结果与我们预期调用顺序一样,checkUser被调用了。其实在我只是把调用实例传到UserService服务中。我们同时在调用addUser时使用了我传入的代理示例调用checkUser方法,之前使用的this调用。
总结:AOP在代理是同类方法使用this就没有走动态代理实现,导致事务不会生效。