在spring中实现事务可以有两种方式
一.编程式事务
最简单的是使用TransactionTemplate
有返回值的用TransactionCallback
无返回值的用TransactionCallbackWithoutResult
二.声明式事务 也就是加入@Transactional注解
Spring 的声明式事务本质上是通过AOP来增强的类的功能
Spring 的AOP本质上就是为类做了一个代理(看似在调用自己写的类,实际用的是增强后的代理类)
访问增强后的代理类,而非直接访问自身的方法
接下来看下面的场景
假如有个接口,它包含两个方法a和b,然后有一个类实现了该接口。在该实现类里在a上标上事务注解、b上不标,此时事务是怎样的?
//接口
interface Service {
void a();
void b();
}
//目标类,实现接口
class ServiceImpl implements Service {
@Override
//如果抛 RollbackException就回滚
@Transactional(rollbackFor = RollbackException.class)
public void a() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}
@Override
public void b() throws RollbackException {
a();
}
}
答案是a方法不会被事务回滚。
因为 @Transactional其实是为你的类做了一个代理,既然是代理类那么只有调用代理类才能真正的
执行到增强的方法,如果是在类的内部调用的化意味着没有调用增强的方法,因此b调用了一个
被@Transactional 注解的方法a但是本身b没有被事务增强所以a也不会有事务的支持。
用代码表示大概如下
//代理类,也要实现相同的接口
class ProxyByJdkDynamic implements Service {
//包含目标对象
private Service target;
public ProxyByJdkDynamic(Service target) {
this.target = target;
}
//目标类中此方法带注解,进行特殊处理
@Override
public void a() {
System.out.println("开启事务");
//调用目标对象的方法,该方法已在事务中了
target.a();
System.out.println("提交事务");
}
//目标类中此方法没有注解,只做简单的调用
@Override
public void b() {
//直接调用目标对象方法
target.b();
}
}
那么如果既想通过b调用a还想a被事务管理应该怎么做?
方法有三种
1.在b方法加入@Transactional注解
2.将接口类注入进来通过接口类来调用a方法
@Autowired
private Service service;
..
@Override
public void b() throws RollbackException{
service.a();
}
3. 在当前类的方法中通过AopContext.currentProxy()获取当前类的代理对象,再调用的代理对象的方法(其实是增强后的方法)
//获取当前代理,这样写避免了自己调用自己的实例
@Override
public void b() throws RollbackException {
((Service) (AopContext.currentProxy())).a();
}