结合Spring框架,在进行数据库操做的时候,常常会使用@Transactional注解,工做的经历中看到不少人使用方式都是错误的,没有深刻理解过其原理,其实这是很危险的!!本篇将深刻源码分析@Transactional注解的工做原理。git
首先从tx:annotation-driven提及。配置了tx:annotation-driven,就一定有对应的标签解析器类,查看NamespaceHandler接口的实现类,能够看到一个TxNamespaceHandler,它注册了AnnotationDrivenBeanDefinitionParser对annotation-driven元素进行解析。github
进入AnnotationDrivenBeanDefinitionParser类,重点看parse方法。
从代码中能够看出,若是tx:annotation-driven中没有配置mode参数,则默认使用代理模式进行后续处理;若是配置了mode=aspectj,则使用aspectj代码织入模式进行后续处理。数据库
本篇分析使用代理模式的代码,进入AopAutoProxyConfigurer.configureAutoProxyCreator方法。
上图代码中标出了一行核心代码,容易被忽略。进入AopNamespaceUtils.registerAutoProxyCreatorIfNecessary方法。
重点关注上图中标出的代码,进入AopConfigUtils.registerAutoProxyCreatorIfNecessary方法。
上图中的代码向Spring容器中注册了一个InfrastructureAdvisorAutoProxyCreator类。可能会疑问为何要注册这个类,有什么做用?查看InfrastructureAdvisorAutoProxyCreator类继承关系。
经过上图中的关系,能够发现InfrastructureAdvisorAutoProxyCreator间接实现了BeanPostProcessor接口,从AbstractAutoProxyCreator类中继承了postProcessAfterInitialization方法。Spring容器在初始化每一个单例bean的时候,会遍历容器中的全部BeanPostProcessor实现类,并执行其postProcessAfterInitialization方法。框架
进入AbstractAutoProxyCreator类的postProcessAfterInitialization方法。
其中wrapIfNecessary方法是建立代理对象的核心方法。
getAdvicesAndAdvisorsForBean方法会遍历容器中全部的切面,查找与当前实例化bean匹配的切面,这里就是获取事务属性切面,查找@Transactional注解及其属性值,具体实现比较复杂,这里暂不深刻分析,最终会获得BeanFactoryTransactionAttributeSourceAdvisor实例,而后根据获得的切面进入createProxy方法,建立一个AOP代理。
进入ProxyFactory.getProxy方法。
createAopProxy方法决定使用JDK仍是Cglib建立代理。
能够看出默认是使用JDK动态代理建立代理,若是目标类是接口,则使用JDK动态代理,不然使用Cglib。这里分析使用JDK动态代理的方式,进入JdkDynamicAopProxy.getProxy方法。
能够看到很熟悉的建立代理的代码Proxy.newProxyInstance。这里要注意的是,newProxyInstance方法的最后一个参数是JdkDynamicAopProxy类自己,也就是说在对目标类进行调用的时候,会进入JdkDynamicAopProxy的invoke方法。源码分析
这里只关注JdkDynamicAopProxy的invoke方法的重点代码。
this.advised.getInterceptorsAndDynamicInterceptionAdvice获取的是当前目标方法对应的拦截器,里面是根据以前获取到的切面来获取相对应拦截器,这时候会获得TransactionInterceptor实例。若是获取不到拦截器,则不会建立MethodInvocation,直接调用目标方法。这里使用TransactionInterceptor建立一个ReflectiveMethodInvocation实例,调用的时候进入ReflectiveMethodInvocation的proceed方法。
代码中的interceptorOrInterceptionAdvice就是TransactionInterceptor的实例,执行invoke方法进入TransactionInterceptor的invoke方法。post
TransactionInterceptor从父类TransactionAspectSupport中继承了invokeWithinTransaction方法。
能够看到,在须要进行事务操做的时候,Spring会在调用目标类的目标方法以前进行开启事务、调用异常回滚事务、调用完成会提交事务。ui
是否须要开启新事务,是根据@Transactional注解上配置的参数值来判断的。若是须要开启新事务,获取Connection链接,而后将链接的自动提交事务改成false,改成手动提交。this
当对目标类的目标方法进行调用的时候,若发生异常将会进入completeTransactionAfterThrowing方法。
Spring并不会对全部类型异常都进行事务回滚操做,默认是只对Unchecked Exception(Error和RuntimeException)进行事务回滚操做。
总结
从上面的分析能够看到,Spring使用AOP实现事务的统一管理,为开发者提供了很大的便利。可是,有部分开发人员会误用这个便利,基本都是下面这两种状况:
1.A类的a1方法没有标注@Transactional,a2方法标注@Transactional,在a1里面调用a2;
2.将@Transactional注解标注在非public方法上。spa
第一种为何是错误用法,缘由很简单,a1方法是目标类A的原生方法,调用a1的时候即直接进入目标类A进行调用,在目标类A里面只有a2的原生方法,在a1里调用a2,即直接执行a2的原生方法,并不经过建立代理对象进行调用,因此并不会进入TransactionInterceptor的invoke方法,不会开启事务。3d
@Transactional的工做机制是基于AOP实现的,而AOP是使用动态代理实现的,动态代理要么是JDK方式、要么是Cglib方式。若是是JDK动态代理的方式,根据上面的分析能够知道,目标类的目标方法是在接口中定义的,也就是必须是public修饰的方法才能够被代理。若是是Cglib方式,代理类是目标类的子类,理论上能够代理public和protected方法,可是Spring在进行事务加强是否可以应用到当前目标类判断的时候,遍历的是目标类的public方法,因此Cglib方式也只对public方法有效。
深刻Class类getMethods方法,能够看到取得是public修饰的方法。