前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解Spring系列之十二:@Transactional是如何工作的

深入理解Spring系列之十二:@Transactional是如何工作的

作者头像
JavaQ
发布2018-04-08 10:12:54
1.1K1
发布2018-04-08 10:12:54
举报
文章被收录于专栏:JavaQJavaQ

结合Spring框架,在进行数据库操作的时候,经常使用@Transactional注解,工作经历中看到很多人使用方式都是错误的,没有深入理解过其原理,这是很危险的!!本篇将深入Spring源码,分析@Transactional注解的工作原理。相信,看完你会点赞转发的!

源码分析

首先从<tx:annotation-driven/>说起。配置了<tx:annotation-driven/>,就必定有对应的标签解析器类,查看NamespaceHandler接口的实现类,可以看到一个TxNamespaceHandler,它注册了AnnotationDrivenBeanDefinitionParser对annotation-driven元素进行解析。

进入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方法。

TransactionInterceptor从父类TransactionAspectSupport中继承了invokeWithinTransaction方法。

可以看到,在需要进行事务操作的时候,Spring会在调用目标类的目标方法之前进行开启事务、调用异常回滚事务、调用完成会提交事务。是否需要开启新事务,是根据@Transactional注解上配置的参数值来判断的。如果需要开启新事务,获取Connection连接,然后将连接的自动提交事务改为false,改为手动提交。当对目标类的目标方法进行调用的时候,若发生异常将会进入completeTransactionAfterThrowing方法。

Spring并不会对所有类型异常都进行事务回滚操作,默认是只对Unchecked Exception(Error和RuntimeException)进行事务回滚操作。

总结

从上面的分析可以看到,Spring使用AOP实现事务的统一管理,为开发者提供了很大的便利。但是,有部分开发人员会误用这个便利,基本都是下面这两种情况:

  1. A类的a1方法没有标注@Transactional,a2方法标注@Transactional,在a1里面调用a2;
  2. 将@Transactional注解标注在非public方法上。

第一种为什么是错误用法,原因很简单,a1方法是目标类A的原生方法,调用a1的时候即直接进入目标类A进行调用,在目标类A里面只有a2的原生方法,在a1里调用a2,即直接执行a2的原生方法,并不通过创建代理对象进行调用,所以并不会进入TransactionInterceptor的invoke方法,不会开启事务。

@Transactional的工作机制是基于AOP实现的,而AOP是使用动态代理实现的,动态代理要么是JDK方式、要么是Cglib方式。如果是JDK动态代理的方式,根据上面的分析可以知道,目标类的目标方法是在接口中定义的,也就是必须是public修饰的方法才可以被代理。如果是Cglib方式,代理类是目标类的子类,理论上可以代理public和protected方法,但是Spring在进行事务增强是否能够应用到当前目标类判断的时候,遍历的是目标类的public方法,所以Cglib方式也只对public方法有效。

深入Class类getMethods方法,可以看到取得是public修饰的方法。

原理都在这了,记得点赞转发哦!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaQ 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档