Spring事务原理
- 启动类上未标注@EnableTransactionManagement,会在META-INF下的spring.factories下扫描到EnabletransactionManageConfig,将EnableTransactionManagement加载进来
- 配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。
- spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transactionl的相关参数进行相关配置注入,这样就在代理中为我们把相关的事务处理掉了(开启正常提交事务,异常回滚事务)。
- 真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
- 本质还是通过动态代理实现
Spring事务的传播机制
- Propagation.REQUIRED
如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
- Propagation.SUPPORTS
如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
- Propagation.MANDATORY
如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
- Propagation.REQUIRES_NEW
重新创建一个新的事务,如果当前存在事务,延缓当前的事务。
- Propagation.NOT_SUPPORTED
以非事务的方式运行,如果当前存在事务,暂停当前的事务。
- Propagation.NEVER
以非事务的方式运行,如果当前存在事务,则抛出异常。
- Propagation.NESTED
如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
事务传播机制原理
- savepoint,子方法回滚会到savepoint
- 子函数生成新事物需要开两个链接
关于加@Transactional注解的方法之间调用,事务是否生效的问题
- 同一类内方法调用,调用方如果没有事务,无论被调用的b()方法是否配置了事务,此事务在被调用时都将不生效
- 个人理解,当从类外调用方法a()时,从spring容器获取到的serviceImpl对象实际是包装好的proxy对象,因此调用a()方法的对象是动态代理对象。而在类内部a()调用b()的过程中,实质执行的代码是this.b(),此处this对象是实际的serviceImpl对象而不是本该生成的代理对象,因此直接调用了b()方法。
- 不同类之间的方法调用,如类A的方法a()调用类B的方法b(),这种情况事务是正常起作用的。只要方法配置了事务,运行中就会开启事务,产生代理。 同一类内方法调用,无论被调用的b()方法是否配置了事务,此事务在被调用时都将不生效。
- 如何解决
- 用@Autowire注入service,创建代理方法不加事务,通过注入service调用事务方法,再调用b方法
- 使用AopContext获取到当前代理类,需要在启动类加上@EnableAspectJAutoProxy(exposeProxy = true)
- 通过ApplicationContext获取bean
Spring事务失效
-
同类方法互调绕过代理对象,导致调用的子方法事务失效
- aop-starter引入@AspectJ代s理,SpringAop代理只能代理接口
- @EnableAspectJAutoProxy,即使没有接口也可以创建动态代理
- 用代理对象调用
- @Transactional注解加在private、 protected会失效
-
- 之所以会失效是因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。
- @Transactional 注解属性 rollbackFor 设置错误
-
- Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务
- 数据源本身不支持事务
事务回调机制
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
public void afterCommit(){
//do stuff right after commit
System.out.println("commit!!!");
}
});