惠州公司网站建设,合肥建站服务,今天的新闻大事,濮阳新闻最新消息这里写自定义目录标题 spring事务的源码分析阅读spring事务源码的前置知识JDBC的事务spring当中和事务相关的对象spring应用程序编码spring事务的源码如何开始研究spring源码当中如何代理bean spring事务的源码分析
最近在研究seata#xff1b;看了一下spring当中的事务有一点… 这里写自定义目录标题 spring事务的源码分析阅读spring事务源码的前置知识JDBC的事务spring当中和事务相关的对象spring应用程序编码spring事务的源码如何开始研究spring源码当中如何代理bean spring事务的源码分析
最近在研究seata看了一下spring当中的事务有一点心得故而来写篇文章分享一下另外对于seata框架的视频讲解在b站上如果对seata比较感兴趣的可以去三连一下还有就是关于这篇文章对应的视频讲解我也传到这个b站账号上 https://space.bilibili.com/419779862
阅读spring事务源码的前置知识
spring事务这块代码写的有点复杂如果需要完全看懂需要一点点前置知识
JDK动态代理的知识spring bean生命周期和后置处理器的一些知识spring Aop的原理
当然如果你不具备这些知识也没关系知识读起来会难一点其中关于JDK动态代理的知识我曾经录过一个2小时的手写JDK动态代理的视频如果你对JDK动态代理不是很了解可以评论区问一下看完一定会非常清晰当然你如果嫌麻烦就可以直接看下图记住图中的结论如果你懂动态代理可以不用看图了 JDBC的事务
正常情况下spring的事务是基于jdbc的事务来的那么我们现在回顾一下jdbc的事务代码其实非常简单
onnection connection getConnection();
connection.setAutoCommit(false);
//执行sql等等操作
//执行完成之后提交
connection.commit();
connection.rollback();其实你仔细想一下会发现事务他是一个非常难以抽象的概念比如一个苹果你去抽象可以定义一个类Apple然后定义一些属性——产地、颜色、价格、口味等等但是事务这个东西他不是一个名词严格意义上他是一个动作甚至不是一个动作是一些列动作提交、或者回滚那么作为spring作者他该如何在spring源码当中来抽象呢——说白了就是该定义一个怎样的类来描述事务这个东西呢诚如前面说的事务是一个动作spring框架要抽象出来比较困难所以如果想搞懂spring源码当这块的代码需要先搞明白关于事务的一些对象在spring当中
spring当中和事务相关的对象
这块相当重要如果想把spring源码当中对事务的操作代码看懂就一定得先好好看看这一章节当然这里可能阅读起来很麻烦甚至你看不懂但是你一定硬着头皮看下去我尽量写详细一点
1、首先spring如果需要操作事务离不开JDBC那一套——也就是首先的需要获取连接但是spring当中默认认为你是使用了数据源也就是DataSourcespring当中定义了一个类TransactionManager——事务管理器里面保存了数据源这里面的dataSouce需要程序员手动配置给他相信你如果做过spring开发就写过这行代码 2、数据源不代表连接具体的连接对象spring当中有一个类DataSourceTransactionObject这个当中包含了Connetion对象下面是我对这个类属性的总结实际spring源码当中封装了很多层后面再来详细说这里只是先列举出来方便我们理解spring作者在设计事务这块的思路和类的设计
DataSourceTransactionObject{previousIsolationLevel;隔离级别savepointAllowed;是否允许savepointcurrentConnection;连接transactionActive;事务是否活跃mustRestoreAutoCommit;是否需要重置自动提交Connection currentConnection;连接信息
}3、事务状态的包装spring当中事务是否只读事务是否完成事务是否同步事务是否是一个新的事务等等这些信息用了一个TransactionStatus的类来封装
TransactionStatus{//事务的状态boolean rollbackOnly false;//是否只读事务boolean completed false;//是否完成Object savepoint底层数据库支持newTransaction; 是否新开的事务newSynchronization 是否同步
} 4、事务的属性也就是程序在定义事务指定的特点比如传播机制、比如事务的回滚异常是否需要指定隔离级别等等在sping当中定义了一个类TransactionAttribute来封装
5、上面四个类怎么关联起来呢spring当中定义了一个类TransactionInfo他把上面四个类关联起来了如果上面你没用看懂你现在就记住一个结论就是spring在操作事务的时候他最后需要得到一个TransactionInfo的对象通过这个对象去作判断比如传播机制是否合理比如异常是否匹配等等行为去提交事务或者回滚事务这五个类的关系大体如下面 TranscationInfo{ //事务信息 包含了事务的所有操作和信息TransactionAttr;//事务属性--程序员配置TransactionManager//事务管理器,包含了数据源TransactionStatus{//事务的状态boolean rollbackOnly false;//是否只读事务boolean completed false;//是否完成Object savepoint底层数据库支持private final Object transaction;//newTransaction; 是否新开的事务newSynchronization 是否同步 DataSourceTransactionObject//和数据源关联的事务对象DataSourceTransactionObject{previousIsolationLevel;隔离级别savepointAllowed 是否允许savepointcurrentConnection;连接transactionActive事务是否活跃mustRestoreAutoCommit是否需要重置自动提交}}
}之所以现在就列举这几个对象是因为这几个对象很重要了spring底层源码关于事务这块基本就是操作这几个对象读者可以先看个大概如果看不懂可以直接记住我上面的结论——就是spring在操作事务的时候他最后需要得到一个TransactionInfo的对象通过这个对象去作判断比如传播机制是否合理比如异常是否匹配等等行为去提交事务或者回滚事务
spring应用程序编码
当然要研究事务需要一个案例springmybatisplusmysql代码我传到文章末尾——需要说明的是我是基于spring源码这个项目搭建的环境所以是gradle如果下载我的代码需要改对应的比如maven这里对代码做一个简单介绍 1、数据源代码–使用spring自带的需要在配置类当中写代码mybatis的配置就不在文章当中说明了参考我传的附件demo
Beanpublic DataSource dataSource(){DriverManagerDataSource dataSource new DriverManagerDataSource();dataSource.setDriverClassName(com.mysql.cj.jdbc.Driver);dataSource.setUrl(jdbc:mysql://127.0.0.1:3306/cloud_demo?useUnicodetruerewriteBatchedStatementstrue);dataSource.setUsername(root);dataSource.setPassword(aaa111);return dataSource;}2、如果要开启spring的声明式事务的支撑需要配置一个事务管理器事务管理器需要设计一个数据源给他原因上文做了说明
Beanpublic DataSourceTransactionManager dataSourceTransactionManager(){DataSourceTransactionManager dstm new DataSourceTransactionManager();dstm.setDataSource(dataSource());return dstm;}3、在配置类上面开启允许事务
Configuration
EnableTransactionManagement
public class Appconfig {
}EnableTransactionManagement这个注解底层原理非常重要下文会详细说明
4、在对应的方法上面加上Transcation注解
Service
public class InventoryAtService{}Transactional(propagation Propagation.REQUIRED)public boolean save(InventoryEntity entity) {}
}那么InventoryAtService这个类就会被spring代理增强其中的save方法在save方法当中加上事务的逻辑
spring事务的源码如何开始研究
其实研究spring是的事务原理无非两个大的问题 1、spring是如何完成拦截和增强的譬如上面InventoryAtService sprring是如何拦截到这个类的如何完成增强的
2、增强的逻辑是什么也就是他怎么开启事务怎么提交事务怎么回滚事务
首先解决第一个问题spring是如何拦截到需要被增强的类——换句话说spring是如何拦截到方法上加了Transactional的类并且增强的但凡有点spring知识肯定知道这是springaop机制关键是aop需要配置切面、切点、通知、连接点等等而我们这里是没有配置切面、没有配置切点、没有配置通知的
1、首先通知其实是不需要我们配置的因为事务这块的通知肯定spring框架自己写的——关于开启提交事务的代码这块业务代码不可能交给程序员去写所以通知肯定是spring内置的
2、那么切面和切点呢回滚一下如果程序员自己定义切面的Aspectj语法是需要新建一个类然后再里面写代码去配置然后spring会把这些切面当中的信息——通知、切点、连接点封装成为一个Advisor对象所以如果你不手动配置切面你也可以给spring提供一个Advisor对象也能完成aop的定义
3、spring事务增强这块就是他提供了一个Advisor对象里面把切面的各种信息封装了所以不需要你手动去写Aspectj语法风格的切面了
4、那么这个对象在哪里呢怎么生效的呢—EnableTransactionManagement查看这个注解的源码
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
Import(TransactionManagementConfigurationSelector.class)
public interface EnableTransactionManagement {
}5、EnableTransactionManagement 当中会Import一个类TransactionManagementConfigurationSelector.class查看这个类的源码 public class TransactionManagementConfigurationSelector extends AdviceModeImportSelectorEnableTransactionManagement {/*** Returns {link ProxyTransactionManagementConfiguration} or* {code AspectJ(Jta)TransactionManagementConfiguration} for {code PROXY}* and {code ASPECTJ} values of {link EnableTransactionManagement#mode()},* respectively.*/Overrideprotected String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) {case PROXY:return new String[] {AutoProxyRegistrar.class.getName(),ProxyTransactionManagementConfiguration.class.getName()};case ASPECTJ:return new String[] {determineTransactionAspectClass()};default:return null;}}6、TransactionManagementConfigurationSelector 这个类一看就是实现了ImportSelector接口这个接口有一个方法 selectImports会返回一个字符串数组如果字符串数组的内容是一个符合标准的全限定类名那么spring会把这个类当做一个bean去进行实例化和初始化到容器当中——其实springboot的自动装配功能就是利用了这个spring提供的扩张点关于ImportSelector我曾经讲过一个50小时的spring源码视频可以直接找博主要这里不在累述你只需要记住一个结论——selectImports方法当中返回的字符串如果是正常的全限定类名则会被容器识别
7、观察TransactionManagementConfigurationSelector 当中的selectImports方法
return new String[] {AutoProxyRegistrar.class.getName(),ProxyTransactionManagementConfiguration.class.getName() }主要返回了两个类AutoProxyRegistrar和ProxyTransactionManagementConfiguration接下来一个一个分析
8、AutoProxyRegistrar 查看源码 在他的registerBeanDefinitions方法当中注册了一个beanDefintion 接着点进去查看源码 这里主要注册了一个类InfrastructureAdvisorAutoProxyCreator查看这个类的源码
发现他其实是一个BeanPostProcessor并且他是集成至AbstractAutoProxyCreator那么就是一个完成代理的后置处理器和AOP类似——说白了InfrastructureAdvisorAutoProxyCreator理论上会对所有的bean进行代理、为什么是理论上呢因为实际过程中他在处理bean的时候会做一些条件判断所以是理论上那么做什么判断呢其实这属于AOP的知识也就是springAOP在完成代理的时候会进行切面当中的连接点信息判断是否需要增强这里也是一样——判断bean是否符合连接点的配置至于连接点的信息在下一个类当中至此导入的第一个类AutoProxyRegistrar的作用解释清楚了
return new String[] {AutoProxyRegistrar.class.getName(),//解释清楚了ProxyTransactionManagementConfiguration.class.getName() }9、连接点在哪里设置的InfrastructureAdvisorAutoProxyCreator会增强bean但是需要提供连接点或者切点信息这些信息就是第二个类ProxyTransactionManagementConfiguration当中去设置的
查看这个类的源码这个类的源码比较多我们一点点解释 Bean(name TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {BeanFactoryTransactionAttributeSourceAdvisor advisor new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource);advisor.setAdvice(transactionInterceptor);if (this.enableTx ! null) {advisor.setOrder(this.enableTx.IntegergetNumber(order));}return advisor;}首先这个类当中第一个方法是一个加了Bean的方法 返回一个 BeanFactoryTransactionAttributeSourceAdvisor类型的bean我们先不去关系这个bean先看这个方法的参数他有两个参数 1、TransactionAttributeSource transactionAttributeSource, 2、TransactionInterceptor transactionInterceptor 由于这是个Bean方法所以这两个参数也是回去spring容器获取所以这两个参数对应肯定也是一个beanspring在下面又写了两个Bean方法来实例化这两个bean 这两个bean是干嘛的 第一个 TransactionAttributeSource 这个bean的作用主要将来用来获取事务属性的比如你配置的传播机制异常信息等等这个bean当中提供了一个方法来获取这些信息
第二个TransactionInterceptor 这个相当重要他是一个方法拦截器他里面有个invoke方法——相当于通知也就是将来你对bean进行代理对方法进行增强的时候的增强逻辑——放到这里就是事务的开启、提交、回滚都会写在这对象的invoke方法当中当然这里他仅仅是个bean他是怎么成为一个通知的呢
回到BeanFactoryTransactionAttributeSourceAdvisor这个bean我们前面说了他需要两个参数上面解释了他两个参数的作用接下来解释这个类这个类去查看他的源码发现他就是一个advisor也就是他里面应该包含切点、通知、连接点信息所以他需要一个TransactionInterceptor 类型的参数作为他的通知 那么他的切点和连接点在哪里呢查看这个类的源码他里面定义了一个属性 这个切点对象就是后续spring在增强时候需要用到的他提供的一个匹配方法来判断当前bean是否符合切点信息——而且这个切点对象当中的规则就是判断方法是否加了Transactional注解——这个判断逻辑下文分析 这里给出一个spring源码截图的证据
至此EnableTransactionManagement 这个注解的功能都搞清楚了 总结一下这个注解主要两个作用 1、导入了一个后置处理器对象来增强bean ——InfrastructureAdvisorAutoProxyCreator 2、导入了一个Advisor对象主要是定义好了哪些bean需要增强增强的逻辑是什么接下来分析如何应用起来的
spring源码当中如何代理bean 上图所示当调用inventoryService的save方法的时候假设inventoryService没有实例化那么spring容器会实例化这个bean会走他的生命周期当走到BeanPostProcessor的postProcessAfterInitialization方法的时候会从容器当中拿出所有的后置处理器依次执行他们的postProcessAfterInitialization方法关于这块属于bean的生命周期知识可以参考我前面写的循环依赖文章前文已经分析过了EnableTransactionManagement 注解会往容器当中注册一个后置处理器——InfrastructureAdvisorAutoProxyCreator那么这个后置处理会在这里起到作用 getBeanPostProcessors()就是获取到所有的后置处理器我们前面通过EnableTransactionManagement 导入的那个后置处理器就会出现在这里换言之如果不加那个注解那么你的Transcation注解就不会生效因为你不加注解容量当中没有InfrastructureAdvisorAutoProxyCreator这后置处理器就无法对bean进行代理 那么在InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization当中如何对bean进行代理的呢 InfrastructureAdvisorAutoProxyCreator会调用父类的postProcessAfterInitialization方法内部会调用wrapIfNecessary方法完成代理这里说明一下其实循环依赖当中不止三个map也就是网上所谓的三级缓存其实不够严谨严格意义是四个map还有就是上图我写了注释的地方——earlyProxyReferences这里不在详细说了将来可以再写一篇文章来详细说说这个问题
接下来查看wrapIfNecessary方法里面代码属于aop源码的部分我曾经在讲过spring源码这里不在详细说只拎取和本文相关的代码作说明 Object[] specificInterceptors getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);getAdvicesAndAdvisorsForBean方法非常重要顾名思义就是通过当前beaninventoryService找到与之对应的Advisor——说白了就是找到定义了需要增强当前bean的Advisor前文说了Advisor相当于切面里面定义了切点和连接点如果连接点符合当前bean那么就会把这个Advisor找出来;那么这里能不能找到呢看一下源码 调用AbstractAdvisorAutoProxyCreator当中getAdvicesAndAdvisorsForBean方法改方法里面继续调用findEligibleAdvisors findEligibleAdvisors方法里面的内容有点多我们逐行解释 1、首先调用List candidateAdvisors findCandidateAdvisors();找到所有的Advisor不管符不符合当前bean都找出来比如你定义了一个Advisor可能是给OrderSerivice服务的但是也会在这里找出来可以查看他里面的源码 2、找打所有的Advisor之后把他们存入一个List当中然后返回并且把这个list在进行遍历找出符合当前bean需求的advisor findAdvisorsThatCanApply方法就是遍历并判断哪些Advisor对象能服务当前对象里面代码最核心 上面是个空壳方法继续调用findAdvisorsThatCanApply
这里有一个是否IntroductionAdvisor类型的判断关于Introduction——AOP我有讲过这里不在累述你可以理解这个基本不会成立所以会进到322行的if当中执行canApply方法 继续执行289行代码 这里的代码比较复杂首先获取了一个MethodMatcher对象是通过pc对象获取的这里的pc对象其实就是我们前面通过EnableTransactionManagement这个注解导入的Advisor对象——BeanFactoryTransactionAttributeSourceAdvisor当中的切点对象怎么理解呢看懂下面这幅图 最后methodMatcher.matches(method, targetClass))调用的就是TransactionAttributeSourcePointcut类当中的matches方法 然后执行
跟着执行 最后执行 至此spring就能找到所有加了Transcation注解的方法所在的类并且对其进行代理