42

@Transactional Spring 事务源码分析

 4 years ago
source link: https://mp.weixin.qq.com/s/PNnMCD68rg8xLfTkL8bo0w
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

@Transactional Spring事务源码分析

在Spring开发中,经常会用到这个注解,但它是怎么起到开启事务的作用的,还一知半解,只知道应该是基于AOP的,今天就来分析一波它的用法和具体实现。

编程式事务和声明式事务

Spring 支持两种事务管理方式:编程式和声明式,编程式是在程序中显式地使用TransactionTemplate来控制事务的开启和提交。

声明式事务是使用@Transactional注解,在类或方法上使用这个注解,就可以起到开启事务的作用,声明式事务是基于AOP的方式,在方法前开启一个事务,在方法执行后进行commit,中间进行一些异常的判断和处理。

相比来说,声明式事务使用起来更加优雅,AOP的方式对代码没有侵入性,比较推荐在日常开发中使用。

声明式事务的用法

下面看一下@Transactional注解的各项参数。

timtout

timtout是用来设置事务的超时时间,可以看到默认为-1,不会超时。

isolation

isolation属性是用来设置事务的隔离级别,数据库有四种隔离级别:读未提交、读已提交、可重复读、可串行化。MySQL的默认隔离级别是可重复读。

readOnly

readOnly属性用来设置该属性是否是只读事务,只读事务要从两方面来理解:它的功能是设置了只读事务后在整个事务的过程中,其他事务提交的内容对当前事务是不可见的。

那为什么要设置只读事务呢?它的好处是什么?可以看出它的作用是保持整个事务的数据一致性,如果事务中有多次查询,不会出现数据不一致的情况。所以在一个事务中如果有多次查询,可以启用只读事务,如果只有一次查询就无需只读事务了。

另外,使用了只读事务,数据库会提供一些优化。

但要注意的是,只读事务中只能有读操作,不能含有写操作,否则会报错。

propagation

propagation属性用来设置事务的传播行为,对传播行为的理解,可以参考如下场景,一个开启了事务的方法A,调用了另一个开启了事务的方法B,此时会出现什么情况?这就要看传播行为的设置了。

REQUIRES_NEW 和 NESTED非常容易混淆,因为它们都是开启了一个新的事务。我去查询了一下它们之间的区别,大概是这样:

REQUIRES_NEW是开启一个完全的全新事务,和当前事务没有任何关系,可以单独地失败、回滚、提交。并不依赖外部事务。在新事务执行过程中,老事务是挂起的。

NESTED也是开启新事务,但它开启的是基于当前事务的 子事务 ,如果失败的话单独回滚,但如果成功的话,并不会立即commit,而是等待外部事务的执行结果,外部事务commit时,子事务才会commit。

rollbackFor

在@Transactional注解中,有一个重要属性是roolbackFor,这是用来判断在什么异常下会进行回滚的,当方法内抛出指定的异常时,进行事务回滚。 rollbackForClassName 也是类似的。

rollbackFor有个问题是默认情况会做什么,以前认为默认会对所有异常进行回滚,但其实默认情况下只对RuntimeException回滚。

noRollbackFor

这个和上面正好相反,用来设置出现指定的异常时,不进行回滚。

声明式事务的实现机制

Spring在调用事务增强器的代理类时会首先执行TransactionInterceptor进行增强,在TransactionInterceptor的invoke方法中完成事务逻辑。首先看下TransactionInterceptor的类图结构。

ZZfM3ae.jpg!web

TransactionInterceptor

在invoke方法里,调用了父类的模板方法invokeWithinTransaction,下面我们看下TransactionAspectSupport类。

createTransactionIfNecessary

可以看到,这个invokeWithinTransaction方法已经包含了事务执行的整个流程,这里是使用了模板模式,具体的实现交给子类去实现。下面我们分析一下其中的重要方法。

getTransaction

下面们看下用来用来获取TransactionStatus的getTransaction方法,这个方法是在AbstractPlatformTransactionManager抽象类中,

可以看到getTransaction是用来做了一些事务的初始化工作,包括一些判断,新建事务等等。

其中一些对已有事务的处理、嵌入式事务的处理的细节,暂时就略过了~

completeTransactionAfterThrowing

下面我们回到TransactionInterceptor,看一下下一个流程:回滚

上面有个rollbackOn(ex)方法,是用来判断回滚类型的,我们看下它的实现,它有不同的实现类,看下默认的

zYzYvqA.jpg!web

确实是RuntimeException和Error。

我们接着看一下回滚的实现。

processRollback

上面的代码中,有保存点的是指的嵌套事务,因为嵌套事务并不是真正的两个事务,所以会有保存点的信息进行回滚。

事务的提交和回滚的流程类似,同样进行了不同事务类型的判断,在此不进行额外的分析。

结语

通过idea的反编译工具,分析了一波代码,同时参考了《Spring源码解析》,练习了一下分析源码的能力,能大体地看出事务的实现过程,大概看过之后感觉Spring这样写是非常合理的,代码非常清晰,每个功能点都拆分地特别细。博客没有分析事务的加载,因为那块和事务本身的处理并没有太大的关系,而是Spring对注解等属性的加载并生成TransactionInterceptor代理对象。

而且Spring里面使用了非常多的模板方法,使用模板方法对我们的好处是,能从大到小、从整体到局部地了解到整个过程,而且大大降低代码的耦合性。以后在编码时也会注意多使用模板方法。

文章同步发表于  https://blog.csdn.net/acingdreamer/article/details/91873745


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK