足够应付面试的Spring事务源码阅读梳理(建议珍藏)

问:把大象放冰箱里,分几步?
答:三步啊,第1、把冰箱门打开,第2、把大象放进去,第3、把冰箱门带上。
问:实现Spring事务,分几步?
答:三步啊,第1、找出须要事务的方法,第2、把事务加进去,第3、执行事务。
You may find it's not a joke, it's serious。
java

Try to find an entrance


当你面对一个彻底不熟悉的事物时,必定要想办法找到一个突破口,而后逐步深刻。那Spring事物的突破口在哪里呢?很明显在@EnableTransactionManagement注解里,由于是它启用了事物功能。
请看下图:
编程


发现注解还引入了一个类TransactionManagementConfigurationSelector。
再来看这个类,以下图:
工具

发现若是采用代理的方式时,又引入了一个类ProxyTransactionManagementConfiguration。this

接着看这个类(3d

重点来了 ),以下图:

发现这个类往容器中注册了3个bean,第一个是BeanFactoryTransactionAttributeSourceAdvisor。它以 Advisor

结尾说明它是Spring AOP范畴里的东西。代理

在AOP里,cdn

Advisor = Pointcut + Advice

,Pointcut是切入点,表示要拦截的方法,Advice是加强,表示要加进去的事物功能。对象

再看看另外两个注册的bean,就是和这两个相关的。其中TransactionInterceptor就是一个Advice,由于它实现了Advice接口,包含了把事物加进去的逻辑。blog

TransactionAttributeSource虽然不是一个Pointcut,可是它被Pointcut所用,用于检测一个类的方法上是否有@Transactional注解,来肯定该方法是否须要事物加强。继承

从下图中也能够看出这一点:


能够看到这个bean经过下面的set方法被设置进去,而后又用在了Pointcut的类里了。
总体来看,此部分的结构和功能划分仍是很是清晰的。下面来逐一研究。

AOP切点


TransactionAttributeSourcePointcut类以Pointcut结尾,说明它是一个切入点,就是标识要被拦截的方法。类名的前缀部分代表了这个切入点的实现原理。
看下这个前缀是TransactionAttributeSource,它以Source结尾,说明它是一个源(即源泉,有向外提供东西的意思)。它的前缀是TransactionAttribute,即事务属性。
因而可知,这个源能够向外提供事务属性,其实就是判断一个类的方法上是否标有@Transactional注解,若是有的话还能够获取这个注解的属性(即事务属性)。
总体来讲就是,Pointcut拦截住了方法,而后使用这个“源”去方法和类上获取事务属性,若是能获取到,说明此方法须要参与事务,则进行事务加强,反之则不加强。
下面这张图能够证实咱们的想法:


能够看出matches方法的两个参数就是一个方法(Method)和一个类(Class<?>)。最后从方法和类上获取事务属性,再进行是否为null判断。
如今这个“源”仍是个黑盒子,下面来揭开它的面纱。它的实现类是AnnotationTransactionAttributeSource,以Annotation开头,说明是基于注解实现的。
下面图是它的源码的一部分:

第一个方法从类上找事务属性,第二个方法从方法上找事务属性,它俩都调用了第三个方法来实现。

PS:咱们都知道,方法上的注解优先级高于类上的,是由于找注解时先找方法上的,找不到时再去类上找。因此方法上的优先级高。此部分代码逻辑在父类里写着呢,这里再也不展现了。

第三个方法使用多个事务注解解析器(TransactionAnnotationParser)去解析注解,为啥是多个解析器呢?由于事务注解不只Spring提供了,Java后来也提供了,就是javax.transaction.Transactional。

Spring对本身注解的解析器实现类是SpringTransactionAnnotationParser,以下图:


能够看出使用工具类来读取注解@Transactional的属性,而后逐个解析出属性值并进行类型转换,接着把这些属性封装到一个类里,这个类其实就是事务属性,即TransactionAttribute。
这个事务属性继承了事务定义接口,事务定义接口咱们应该都很熟悉,以下图:

这也证实了之前文章里说过的话,@Transactional注解的做用有两个, 一是代表要参与事务 ,二是代表如何参与事务

,这些注解属性就是来规定如何参与的。

这个事务属性TransactionAttribute是个接口,它的实现类在这里就再也不详说了。

AOP加强


Advice就是AOP中的加强,TransactionInterceptor实现了Advice接口,因此它就是事务加强。
先来看下该接口,以下图:

发现它只是一个空的标记接口。并且它的包名是 org.aopalliance

,是一个AOP联盟组织,它制定的AOP规范。

先来了解下AOP领域的一些相关内容,Pointcut是切入点,表示要拦截的方法。

它是一个静态的概念 , 即程序不运行时它也是存在的

那么在真正运行时,已经拦截住了,此时该怎么表示这个状况呢?是用Joinpoint来表示的,因此

Joinpoint是一个运行时的概念 , 只有在运行时才存在

请看Joinpoint接口,以下图:


第一个方法proceed()是“继续”的意思,调用它表示去执行被拦截住的方法自己,返回方法自己的返回值。
第二个方法getThis()是获取this对象,即方法运行时所在的目标对象。若是是静态方法,则为null,由于静态方法是属于类自己的,运行时不须要对象。
第三个方法getStaticPart(),其实就表示了被拦截住的方法,即就是一个Method。Method其实算是“元数据”,是属于类型自己的,也有“静态”的意思。
再看一个接口,Invocation,它继承了Joinpoint,以下图:


方法getArguments()就表示运行时传递给被拦截住方法的参数。
再看一个接口,MethodInvocation,它继承了Invocation,以下图:

方法getMethod()返回一个Method,它就是当前正在执行的方法,是对本拦截方法的一个友好实现,返回相同的结果。

可见MethodInvocation接口已经包含了一个方法调用的全量信息,

方法 , 参数 , 目标对象

。这其实就是运行时被拦截住的东西。

再看下面这个接口,MethodInterceptor,方法拦截器,以下图:

它只有一个方法invoke,方法参数就是上面介绍的MethodInvocation。 因此拦截器可使用这个参数来对目标方法进行调用 , 固然在调用前/后能够加入本身的逻辑

TransactionInterceptor类就实现了这个接口,

所以能够在对目标方法的调用先后插入事务逻辑代码来进行事务加强

下面是事务拦截器对该方法的实现,以下图:


它调用的invokeWithinTransaction方法是在父类里的,看下图:

这个图里作的事情较多,逐个来看:

前两行获取事务属性“源”,再用这个“源”来获取事务属性。

咦,有点奇怪,上面不是已经获取过了吗?

是的,上面是在Pointcut里获取的,那只是用于判断那个方法是否要被拦截而已。这里获取的属性才是真正用于事务的。

第三行是根据事务属性,来肯定出一个事务管理器来。

接下来是使用事务管理器打开事务。

接下来是对被拦截住的目标方法的调用执行,固然要try/catch住这个执行。

若是抛出了异常,则进行和异常相关的事务处理,而后将这个异常继续向上抛出。

若是没有抛出异常,则进行事务提交。

最后的else分支是对编程式事务的调用,事务的打开/提交/回滚是开发人员本身写代码控制,因此就不须要事务管理器操心了。

下面请看和异常相关的事务处理,以下图:

判断异常类型是否须要回滚,须要的话就回滚事务,不须要的话就继续提交事务。

这里的总体结构和逻辑流程也是比较清晰的,那是由于一方面得益于AOP领域的概念,另外一方面是事务管理器屏蔽了事务的全部复杂性。

相关文章
相关标签/搜索