Spring事务原理

189 阅读3分钟

Spring事务原理

  1. 启动类上未标注@EnableTransactionManagement,会在META-INF下的spring.factories下扫描到EnabletransactionManageConfig,将EnableTransactionManagement加载进来
  2. 配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。
  3. spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transactionl的相关参数进行相关配置注入,这样就在代理中为我们把相关的事务处理掉了(开启正常提交事务,异常回滚事务)。
  4. 真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
  5. 本质还是通过动态代理实现

Spring事务的传播机制

  • Propagation.REQUIRED

如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

  • Propagation.SUPPORTS

如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

  • Propagation.MANDATORY

如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

  • Propagation.REQUIRES_NEW

重新创建一个新的事务,如果当前存在事务,延缓当前的事务。

  • Propagation.NOT_SUPPORTED

以非事务的方式运行,如果当前存在事务,暂停当前的事务。

  • Propagation.NEVER

以非事务的方式运行,如果当前存在事务,则抛出异常。

  • Propagation.NESTED

如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。

事务传播机制原理

  1. savepoint,子方法回滚会到savepoint
  2. 子函数生成新事物需要开两个链接

关于加@Transactional注解的方法之间调用,事务是否生效的问题

  • 同一类内方法调用,调用方如果没有事务,无论被调用的b()方法是否配置了事务,此事务在被调用时都将不生效
  • 个人理解,当从类外调用方法a()时,从spring容器获取到的serviceImpl对象实际是包装好的proxy对象,因此调用a()方法的对象是动态代理对象。而在类内部a()调用b()的过程中,实质执行的代码是this.b(),此处this对象是实际的serviceImpl对象而不是本该生成的代理对象,因此直接调用了b()方法。
  • 不同类之间的方法调用,如类A的方法a()调用类B的方法b(),这种情况事务是正常起作用的。只要方法配置了事务,运行中就会开启事务,产生代理。 同一类内方法调用,无论被调用的b()方法是否配置了事务,此事务在被调用时都将不生效。
  • 如何解决
    1. 用@Autowire注入service,创建代理方法不加事务,通过注入service调用事务方法,再调用b方法
    2. 使用AopContext获取到当前代理类,需要在启动类加上@EnableAspectJAutoProxy(exposeProxy = true)
    3. 通过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!!!");

    }
});