SpringBoot中事务执行原理分析(二)

简介: SpringBoot中事务执行原理分析(二)

关联博文:

SpringBoot中事务执行原理分析(一)

SpringBoot中事务执行原理分析(二)

SpringBoot中事务执行原理分析(三)

SpringBoot中事务执行原理分析(四)

SpringBoot中事务执行原理分析(五)

SpringBoot中事务执行原理分析(六)

SpringBoot中事务执行原理分析补充篇

你认真研究过Spring中的@EnableTransactionManagement注解吗?


接上文SpringBoot中事务执行原理分析(一)分析后,我们本文详细跟踪下TransactionInterceptor是如何控制事务行为的。


前面我们提到了,我们的service被包装为代理。当触发目标方法时首先触发了DynamicAdvisedInterceptor的intercept方法,我们就从这里开始跟踪。


31224db6cb3e46fbb74fa3e699a1bcc6.png


【1】前置流程

① 获取方法的拦截器链

首先触发下面这行代码获取方法的拦截器链。

// CglibAopProxy.DynamicAdvisedInterceptor#intercept
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);


其本质就是从advised中获取到Advisor然后遍历、判断是否与当前目标类的方法匹配,如果匹配则获取MethodInterceptor。



关于为目标方法获取拦截器链的详细流程,可以参考博文Spring AOP如何为目标方法创建拦截器链?

本文这里获取到的拦截器链如下图所示只有一个TransactionInterceptor:



② ReflectiveMethodInvocation的proceed

如果拦截器链不为空,那么将会走到ReflectiveMethodInvocation的proceed方法。在Spring AOP中CGLIB代理对象增强通知执行原理我们详细跟踪过拦截器链的调用。这里我们见到回滚一下。方法如下所示,其会遍历拦截器链中的每一个元素尝试触发其invoke方法,如果拦截器链执行完,那么将会通过invokeJoinpoint()来触发真正目标方法的执行。

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);
  }
}



如下所示,这里我们将会触发TransactionInterceptor的invoke方法。



【2】核心流程分析

TransactionInterceptor的invoke方法如下所示,首先获取targetClass ,本文这里是class com.recommend.service.impl.SysAdviceServiceImpl。然后触发了invokeWithinTransaction方法。

@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, invocation::proceed);
}


方法源码如下所示,这里根据本文情况忽略了一些分支:

// TransactionAspectSupport#invokeWithinTransaction
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
    final InvocationCallback invocation) throws Throwable {
  // If the transaction attribute is null, the method is non-transactional.
// 获取TransactionAttributeSource ,用于解析事务注解属性,其维护了annotationParsers
  TransactionAttributeSource tas = getTransactionAttributeSource();
//获取事务注解的属性
  final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//根据事务注解属性信息获取事务管理器TransactionManager,本文获取的是DataSourceTransactionManager
  final TransactionManager tm = determineTransactionManager(txAttr);
//响应式事务管理,本文跳过这个
//...
//转换为为PlatformTransactionManager
  PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
//获取方法的描述信息 com.recommend.service.impl.SysAdviceServiceImpl.testSave
  final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
//这里是事务处理逻辑
  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.
  //触发CglibMethodInvocation的proceed方法,
  //其又会走到父类ReflectiveMethodInvocation的proceed方法
      retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
      // target invocation exception 触发事务回滚
      completeTransactionAfterThrowing(txInfo, ex);
      throw ex;
    }
    finally {
    // 清理事务信息
      cleanupTransactionInfo(txInfo);
    }
    if (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;
  }
//下面是处理CallbackPreferringPlatformTransactionManager类型,我们可以忽略
  //...
}


方法逻辑梳理如下:


获取TransactionAttributeSource ,用于解析事务注解属性,其维护了annotationParsers

获取事务注解的属性

根据事务注解属性信息获取事务管理器TransactionManager,本文获取的是DataSourceTransactionManager

判断是否为响应式事务,如果是则走该分支,本文跳过这个

将事务管理器转换为为PlatformTransactionManager

获取方法的描述信息 本文这里是com.recommend.service.impl.SysAdviceServiceImpl.testSave

如果txAttr为null,或者事务管理器不是CallbackPreferringPlatformTransactionManager

获取事务对象,这里将会触发事务的创建

invocation.proceedWithInvocation()最终会触发目标方法

如果抛出异常触发回滚并抛出异常

清理事务信息

如果没有抛出异常则提交事务

触发CallbackPreferringPlatformTransactionManager的分支

invocation.proceedWithInvocation()方法将会触发CglibMethodInvocation的proceed方法,后置其又会走到父类ReflectiveMethodInvocation的proceed方法,将会触发下一个拦截器。如果没有拦截器(本文这里只有一个拦截器)将会触发真正目标方法的调用。


daed47c6f0894b73b77395f883b44393.png



核心流程如下图



我们接下来逐个分析核心流程的模板方法:


获取事务对象 createTransactionIfNecessary

提交事务 commitTransactionAfterReturning(txInfo);

回滚事务 completeTransactionAfterThrowing(txInfo, ex);

清理事务信息 cleanupTransactionInfo(txInfo);其中清理事务信息较为简单,如下所示其重置了当前线程持有的事务信息。

protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
  if (txInfo != null) {
    txInfo.restoreThreadLocalStatus();
  }
}
private void restoreThreadLocalStatus() {
  // Use stack to restore old transaction TransactionInfo.
  // Will be null if none was set.
  transactionInfoHolder.set(this.oldTransactionInfo);
}
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
    new NamedThreadLocal<>("Current aspect-driven transaction");



transactionInfoHolder是一个ThreadLocal对象,持有了当前线程上下文的事务信息,这里restoreThreadLocalStatus其实就是将Holder持有的事务信息恢复到前一个事务信息oldTransactionInfo

目录
相关文章
|
2月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
37 0
|
10天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
17天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
67 14
|
2月前
|
Java Spring
SpringBoot自动装配的原理
在Spring Boot项目中,启动引导类通常使用`@SpringBootApplication`注解。该注解集成了`@SpringBootConfiguration`、`@ComponentScan`和`@EnableAutoConfiguration`三个注解,分别用于标记配置类、开启组件扫描和启用自动配置。
62 17
|
2月前
|
Java 容器
springboot自动配置原理
启动类@SpringbootApplication注解下,有三个关键注解 (1)@springbootConfiguration:表示启动类是一个自动配置类 (2)@CompontScan:扫描启动类所在包外的组件到容器中 (3)@EnableConfigutarion:最关键的一个注解,他拥有两个子注解,其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中,@Import会导入一个自动配置文件选择器,他会去加载META_INF目录下的spring.factories文件,这个文件中存放很大自动配置类的全类名,这些类会根据元注解的装配条件生效,生效
|
3月前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
130 4
|
4月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot
|
3月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
704 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
3月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
262 2
|
3月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
199 1