1.故事前因

在分析mybatis源码时一直带的疑问,一直以为事务是在SqlSessionTemplate#SqlSessionInterceptor#invoke完成的,直到断点才发现并不简单!

  • 在没有事务注解时连接数据库都是SimpleExecutor#prepareStatementgetConnection连接的,这里何来的事务一说,下一步都是直接查询了
  • 然后在有事务注解的时候,经过断点发现这个连接早就创建好了,那么是何时创建的?事务又是在哪里开启的呢?
    今天来一探究竟!!!

2.首先再回顾一下aop原理

  • 大家都知道首先向容器注册一个事务管理类,同时扫描所有的@Transactional注解并生成代理类。然后再执行加了事务的代码是会先走动态代理的代码。

2.1 首先找到bean初始化之后的回调AbstractAutowireCapableBeanFactory

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {//将原始类进行代理包装Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}

2.2 来到AOP处理类AbstractAutoProxyCreator#wrapIfNecessary

  • wrapIfNecessary中找到这段代码
// Create proxy if we have advice. 如果有advice就创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//满足添加就创建代理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;
}//getAdvicesAndAdvisorsForBean -> findEligibleAdvisors -> findCandidateAdvisors
//-> BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
public List<Advisor> findAdvisorBeans() {// Determine list of advisor bean names, if not cached already.String[] advisorNames = this.cachedAdvisorBeanNames;if (advisorNames == null) {// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the auto-proxy creator apply to them!//找到Advisor实现类的beanName数组advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);this.cachedAdvisorBeanNames = advisorNames;}if (advisorNames.length == 0) {return new ArrayList<>();}List<Advisor> advisors = new ArrayList<>();for (String name : advisorNames) {//。。。省略部分代码//通过beanName找到所有的Advisor实现类beanadvisors.add(this.beanFactory.getBean(name, Advisor.class));//。。。省略部分代码     return advisors;
}

2.3 AOP的调用阶段

  • 上述内容会包装成一个config最终由JdkDynamicAopProxy或者CglibAopProxy生成动代理,代理invoke类自然就是这两个类,本内容大概分析一下JdkDynamicAopProxy,在该类找到invoke类
//找到这个代码 将advised的实现类根据满足规则 最终组成一条调用链路
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//封装成MethodInvocation
MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();//ReflectiveMethodInvocation 递归调用代理类
public Object proceed() throws Throwable {// We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {//链路调用完成后 最终调用原始类return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {//调用满足条件的切面类return dm.interceptor.invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.//递归return proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}
}

3.事务的源码分析

3.1 事务的初始化类ProxyTransactionManagementConfiguration

//加了Configuration 能被spring扫描到
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {//该类实现了Advisor beanName为org.springframework.transaction.config.internalTransactionAdvisor//也就是上面的切面扫描类会扫描到该类@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();//校验规则 都是由下面的bean实现初始化的advisor.setTransactionAttributeSource(transactionAttributeSource);//链路类advisor.setAdvice(transactionInterceptor);if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {//链路执行类为TransactionInterceptorTransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}
}
  • 简单分析下AnnotationTransactionAttributeSource
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;if (jta12Present || ejb3Present) {this.annotationParsers = new LinkedHashSet<>(4);this.annotationParsers.add(new SpringTransactionAnnotationParser());if (jta12Present) {this.annotationParsers.add(new JtaTransactionAnnotationParser());}if (ejb3Present) {this.annotationParsers.add(new Ejb3TransactionAnnotationParser());}}else {this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());}
}//都会添加该类SpringTransactionAnnotationParser 只贴这个方法了 根据这个判断是否生成事务的代理类
@Override
public boolean isCandidateClass(Class<?> targetClass) {return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
}

至此,整个AOP的流程已经事务类的初始化以及全部分析完了,接下来继续分析链路中的事务类调用

3.2 事务代理类TransactionInterceptor

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...//执行return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {@Override@Nullablepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}@Overridepublic Object getTarget() {return invocation.getThis();}@Overridepublic Object[] getArguments() {return invocation.getArguments();}});
}

3.3 事务管理类TransactionAspectSupport#invokeWithinTransaction

if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.//创建一个事务 主要分析这里 ##TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.//业务执行 这里如果无其他切面正常会到SqlSessionTemplateretVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception//事务异常切面completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清除事务相关cleanupTransactionInfo(txInfo);}if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {// Set rollback-only in case of Vavr failure matching our rollback rules...TransactionStatus status = txInfo.getTransactionStatus();if (status != null && txAttr != null) {retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);}}//提交或者回滚事务commitTransactionAfterReturning(txInfo);return retVal;
}

3.4 事务入口AbstractPlatformTransactionManager

  • createTransactionIfNecessary -> AbstractPlatformTransactionManager#getTransaction -> startTransaction
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// ###doBegin(transaction, definition);prepareSynchronization(status, definition);return status;
}

3.5 事务开启DataSourceTransactionManager#doBegin

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {//根据数据源插件获取一个连接  Connection newCon = obtainDataSource().getConnection();if (logger.isDebugEnabled()) {logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");}txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);txObject.setReadOnly(definition.isReadOnly());// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already).//如果是自动提交的if (con.getAutoCommit()) {//设置必须有序提交txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}//同时关闭自动提交con.setAutoCommit(false);}//开始事务的入口 stmt.executeUpdate("SET TRANSACTION READ ONLY"); ###prepareTransactionalConnection(con, definition);//设置事务的活跃状态txObject.getConnectionHolder().setTransactionActive(true);//。。。省略后续代码
}//prepareTransactionalConnection
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)throws SQLException {if (isEnforceReadOnly() && definition.isReadOnly()) {try (Statement stmt = con.createStatement()) {//直接执行sql 事务开始后 取决于数据库隔离级别 默认是行锁stmt.executeUpdate("SET TRANSACTION READ ONLY");}}
}

3.6 事务之rollback

  • TransactionAspectSupport#commitTransactionAfterReturning -> AbstractPlatformTransactionManager#commit -> processRollback - > doRollback -> DataSourceTransactionManager#con.rollback()
  • ConnectionImpl#rollback
private void rollbackNoChecks() throws SQLException {synchronized (getConnectionMutex()) {if (this.useLocalTransactionState.getValue()) {if (!this.session.getServerSession().inTransactionOnServer()) {return; // effectively a no-op}}//执行sqlthis.session.execSQL(null, "rollback", -1, null, false, this.nullStatementResultSetFactory, null, false);}}

3.7 事务之commit

  • TransactionAspectSupport#commitTransactionAfterReturning ->AbstractPlatformTransactionManager#commit -> processCommit- > doCommit -> DataSourceTransactionManager#con.commit()
  • ConnectionImpl#commit
//执行sql
this.session.execSQL(null, "commit", -1, null, false, this.nullStatementResultSetFactory, null, false);

4 总结

1.事务是根据Spring的AOP机制去完成的
2.AOP生是根据类生成代理类的,所以事务代码块执行时使用this.addMoney(事务块代码)是无效的,只能重新获取该代理类执行事务块代码才会生效
3.事务是每次处理完都会被回收掉的。

以上就是本章的全部内容了。

上一篇:mybatis第九话 - 手写实现一个简单的mybatis版本
下一篇:mybatis第十一话 - mybaits getConnection连接数据库的源码分析

贵有恒何必三更眠五更起,最无益只怕一日曝十日寒

mybatis第十话 - mybaits整个事务流程的源码分析相关推荐

  1. activiti学习(二十一)——流程虚拟机源码分析(三)——从进入到离开userTask

    前言 承接上文<activiti学习(二十)--流程虚拟机源码分析(二)--从开始节点离开到下个节点前>,假设execution接下来进入的节点是userTask,本文分析一下进入user ...

  2. 抽象同步器AQS、CAS应用之--ReentrantLock,lock和unlock的流程、源码分析

    文章目录 1. AQS和CAS 1.1 CAS存在的bug:ABA问题 2. ReentrantLock和synchronized的区别 3. ReentrantLock的内部结构 3.1 lock. ...

  3. Struts2请求处理流程及源码分析

    Struts2请求处理流程及源码分析 根据Web.xml配置,请求首先经过ActionContextCleanUp过滤器,其为可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助(Site ...

  4. 【SA8295P 源码分析】18 - Camera Bringup 流程 及 源码分析

    [SA8295P 源码分析]18 - Camera Bringup 流程 及 源码分析 一.Camera Bringup 流程 1.1 CameraConfigSA8295.c 配置文件解析 1.2 ...

  5. @Transactional的用法详解及Transactional事务无效的源码分析

    数据库事务正确执行的四要素 1.原子性 事务是不可分割的最小的工作单元,事务内的操作要么全做,要么全不做,不能只做一部分. 2.一致性 事务执行前数据库的数据按照逻辑处于正确的状态,事务执行后数据库的 ...

  6. 深入SpringAOP实现流程(源码分析)

    系列文章目录 SpringAOP实现 深入SpringAOP实现流程(源码分析) 使用递归算法+责任链模拟AOP底层通知调用 文章目录 系列文章目录 一.SpringAOP实现(源码追踪) 1.@En ...

  7. 阿里开源一站式分布式事务框架seata源码分析(AT模式下TM与RM分析)

    序言: 对于阿里开源分布式事务框架seata的详细了解可以参考官网,这里不会详细介绍.本章只会介绍seata中AT模式的源码分析(对阿seata有一定了解或者成功完成过demo). seata中一个事 ...

  8. Ceph 学习——OSD读写流程与源码分析(一)

    消息从客户端发送而来,之前几节介绍了 客户端下 对象存储.块存储库的实现以及他们在客户端下API请求的发送过程(Ceph学习--Librados与Osdc实现源码解析 . Ceph学习--客户端读写操 ...

  9. android字符显示流程图,Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

最新文章

  1. hdf heg 批量拼接_[转载]MODIS Aerosol product/MODIS气溶胶产品
  2. java extjs如何使用_ExtJS初探:在项目中使用ExtJS
  3. JavaScriptjQuery.变量作用域
  4. java 后台路线学习
  5. 如何用socket构建一个简单的Web Server
  6. C 语言资源大全中文版
  7. java 不同分辨率_java9新特性-14-多分辨率图像 API
  8. centos配置网络笔记
  9. 你以为面试官问的是分布式缓存,其实他想问……
  10. 我的英语六级考试成绩
  11. Tomcat 配置数据库连接池
  12. DPDK - 海光(hygon) 3250 l2fwd 性能测试
  13. 【presto】presto 查询hive分桶表问题
  14. 情景式领导力学习(1) - 介绍
  15. tagged和untagged
  16. android网卡调试命令
  17. “linux系统内网穿透、外网访问”教程
  18. 最重要的十年做什么才不浪费?
  19. 微信小程序开发之路(十三)正式开工--设计ER图与数据库的创建
  20. surfacepro3运行C语言,Surface Pro 6 体验,没有 USB-C

热门文章

  1. 新MAC苹果M1芯片简要分析(是不是地表最强呢??)
  2. 市场调研-全球与中国VR和AR光学器件市场现状及未来发展趋势
  3. CO-PA: 获利能力分析之特征值派生 - KEDR / KEDB
  4. WEB2.0下的金蛋 什么样的商业模式会成功?
  5. Windows配置环境变量
  6. Hexo+Butterfly主题美化
  7. UG NX 12 抽取几何特征
  8. 图片拼图微信小程序源码_支持多模板制作和流量主
  9. 明星艺人宣传推广的有效方案有哪些呢?
  10. 【ES6】Promise