当前位置: 首页 > news >正文

一文学习 Spring 声明式事务源码全流程总结

Spring 声明式事务源码学习全过程

in short 四步走

  1. Srping 如何从配置中加载的入口
  2. Spring 声明式事务的相关的 BeanDefinition加载流程
  3. Spring 声明式事务的相关对象创建流程
  4. Spring 声明式事务的拦截调用的过程(包括: 方法嵌套, 事务传播属性的处理过程)

最后再看看第三方的框架是如何支持 Spring 声明式事务, 给Spring 托管事务的

一、Spring 声明式事务的入口点

对于XML入口点

XML配置定义

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><tx:annotation-driven></tx:annotation-driven><!-- dao bean--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref = "dataSource"></property></bean><!-- service bean--><bean id="bookService" class="org.yang.learn.spring.tx.BookService" ><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean><!-- 数据源 bean--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url"value="jdbc:mysql://192.168.40.171:3306/workflow_test?serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8&amp;useSSL=false"/><property name="username" value="user_dev"></property><property name="password" value="dev-sny.com"></property></bean><!-- 事务管理bean--><bean id="transactionManager" class="org.springframework.jdbc.support.JdbcTransactionManager" ><constructor-arg ref="dataSource"></constructor-arg></bean><aop:config><!--切点配置	--><aop:pointcut id="serviceOperation"expression="execution(* org.yang.learn.spring.tx.BookService.*(..))"/><!-- 通知/增强 配置 (关键是这个通知指向 txAdvice '事务增强')--><aop:advisor pointcut-ref="serviceOperation"  advice-ref="txAdvice"/></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="get*" read-only="true"/><tx:method name="insertWithTransaction" propagation="REQUIRED" /><tx:method name="insertWithNoTransaction" propagation="NEVER" /></tx:attributes></tx:advice>
</beans>

在XML中配置 tx:.. 启用tx标签, 在解析XML自定义标签时, 会拿到 TxNamespaceHandler 命名空间处理器, 其主要工作就是注册事务相关的标签的解析器

  • tx:advice 标签解析器:负责XML相关的标签解析 TxAdviceBeanDefinitionParser
  • tx:annotation-driven 标签解析器:负责注解相关的解析 AnnotationDrivenBeanDefinitionPar

org.springframework.transaction.config.TxNamespaceHandler

public class TxNamespaceHandler extends NamespaceHandlerSupport {static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}@Overridepublic void init() {//  <tx:advice> 标签解析器:负责解析XML <tx:advice> 事务标签配置 TxAdviceBeanDefinitionParserregisterBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());//  <tx:annotation-driven> 标签解析器:负责解析注解相关的事务配置 AnnotationDrivenBeanDefinitionParserregisterBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());// JTA 规范的分布式事务管理器(管理跨多个资源的事务) TODOregisterBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}
}

启用事务注解支持

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {/*** Parses the {@code <tx:annotation-driven/>} tag. Will* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}* with the container as necessary.*/@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj"registerTransactionAspect(element, parserContext);if (ClassUtils.isPresent("jakarta.transaction.Transactional", getClass().getClassLoader())) {registerJtaTransactionAspect(element, parserContext);}}else {// 默认是  proxy 模式// mode="proxy"AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}

注册三剑客

/*** Inner class to just introduce an AOP framework dependency when actually in proxy mode.*/
private static class AopAutoProxyConfigurer {public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {Object eleSource = parserContext.extractSource(element);// Create the TransactionAttributeSource definition.RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// Create the TransactionInterceptor definition.RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registerTransactionManager(element, interceptorDef);interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);/*** 其中构建事务增强器(BeanFactoryTransactionAttributeSourceAdvisor)* - **Pointcut(切点)**: 默认匹配所有标注 `@Transactional` 的类 / 方法(由 `TransactionAttributeSourcePointcut` 实现)* - **Advice(通知)**: 即 `TransactionInterceptor`(事务拦截器)* - **TransactionAttributeSource(注解解析器)**:即 `AnnotationTransactionAttributeSource`, 负责解析 `@Transactional` 注解的属性(传播行为、隔离级别等)。*/// Create the TransactionAttributeSourceAdvisor definition.RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);if (element.hasAttribute("order")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));parserContext.registerComponent(compositeDef);}}
}

事务注解支持的三剑客

  1. AnnotationTransactionAttributeSource
    ↓(解析注解)
  2. TransactionInterceptor
    ↓(执行事务逻辑)
  3. BeanFactoryTransactionAttributeSourceAdvisor
    ↓(组装切点+通知)

总结一下XML入口, 就是无论 xml 支持 还是注解支持都会构造 org.springframework.transaction.interceptor.TransactionInterceptor 这个核心 advice 事务拦截器

对于注解的入口

@EnableTransactionManagement
public class TXMain {@Transactionalpublic static void main(String[] args) throws Exception {System.out.println("==========================================================");//ApplicationContext context = new ClassPathXmlApplicationContext("application-tx.xml");AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TXMain.class);BookService bookService = context.getBean("bookService", BookService.class);Book book = new Book();book.setName("30秒精通javascript,一分钟精通java");book.setCode(""+System.currentTimeMillis());
//		bookService.insertWithTransaction(book );bookService.insertWithNoTransaction(book);System.out.println("bookService = "+bookService);System.out.println("bookService getList = "+bookService.getList());System.out.println("==========================================================");}
}

@EnableTransactionManagement 注解导入了 TransactionManagementConfigurationSelector 默认选中的是 ProxyTransactionManagementConfiguration

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {/*** 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) {return switch (adviceMode) {case PROXY -> new String[] {AutoProxyRegistrar.class.getName(),ProxyTransactionManagementConfiguration.class.getName()};case ASPECTJ -> new String[] {determineTransactionAspectClass()};};}private String determineTransactionAspectClass() {return (ClassUtils.isPresent("jakarta.transaction.Transactional", getClass().getClassLoader()) ?TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);}}

同样注解的三剑客

  1. AnnotationTransactionAttributeSource
    ↓(解析注解)
  2. TransactionInterceptor
    ↓(执行事务逻辑)
  3. BeanFactoryTransactionAttributeSourceAdvisor
    ↓(组装切点+通知)

org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration

/** Copyright 2002-2021 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.transaction.annotation;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.context.annotation.Role;
import org.springframework.transaction.config.TransactionManagementConfigUtils;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;/*** {@code @Configuration} class that registers the Spring infrastructure beans* necessary to enable proxy-based annotation-driven transaction management.** @author Chris Beams* @author Sebastien Deleuze* @since 3.1* @see EnableTransactionManagement* @see TransactionManagementConfigurationSelector*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ImportRuntimeHints(TransactionRuntimeHints.class)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)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.<Integer>getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {// Accept protected @Transactional methods on CGLIB proxies, as of 6.0.return new AnnotationTransactionAttributeSource(false);}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}}

总结流程图

image

二、事务相关的 BeanDefinition 解析过程 (XML)

bean 标签

对于 jdbcTemplate transactionManager dataSource bookService 走的是默认命名空间的处理器, IOC标准解析流程, 不再啰嗦了
[[Spring IOC 源码学习 XML详细加载流程总结]]
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element ele) {//是否 是元素标签/*** 处理默认命名空间的标签, 有如下四个* <import></import>, <alias> </alias>, <bean></bean>, <beans></beans>**/if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else {/*** 处理 非默认命名空间的标签;* 	注意这里包括 <context:bean ...> <aop:xx ...> <tx:xx ...> 等等所有指定命名空间的xml配置* 	主要逻辑是: 拿到元素的命名空间URI, 再从 XmlReaderContext 找到对应的 NamespaceHandler 调用解析 `parse`方法解析到 BeanDefinition 返回*/delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}
}

aop 标签

对于 aop 部分的标签则的是 AOP 的流程

<aop:config><!--切点配置	--><aop:pointcut id="serviceOperation"expression="execution(* org.yang.learn.spring.tx.BookService.*(..))"/><!-- 通知/增强 配置 (关键是这个通知指向 txAdvice '事务增强')--><aop:advisor pointcut-ref="serviceOperation"  advice-ref="txAdvice"/>
</aop:config>
  1. <aop:pointcut... : 解析为 org.springframework.aop.aspectj.AspectJExpressionPointcut 其 BeanDefinition
  2. <aop:advisor... : 解析为 org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor 其 BeanDefinition

internalAutoProxyCreator 的注册

[[Spring AOP 源码学习 详细流程总结]]

这里要注意AOP 的 ConfigBeanDefinitionParser 在解析时是会注册的一个internalAutoProxyCreator! (AOP解析流程, 在BPP回调时创建代理对象的)
org.springframework.aop.config.ConfigBeanDefinitionParser#parse

	@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {CompositeComponentDefinition compositeDef =new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));parserContext.pushContainingComponent(compositeDef);/*** 1. 注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的* 名称是: org.springframework.aop.config.internalAutoProxyCreator* 对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST*	    InfrastructureAdvisorAutoProxyCreator.class,AspectJAwareAdvisorAutoProxyCreator.class, AnnotationAwareAspectJAutoProxyCreator.class* 	注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的**/configureAutoProxyCreator(parserContext, element);

解析 <aop:pointcut>, <aop:advisor>, 没有切面标签
org.springframework.aop.config.ConfigBeanDefinitionParser#parse

@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {CompositeComponentDefinition compositeDef =new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));parserContext.pushContainingComponent(compositeDef);/*** 1. 注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的* 名称是: org.springframework.aop.config.internalAutoProxyCreator* 对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST*	    InfrastructureAdvisorAutoProxyCreator.class,AspectJAwareAdvisorAutoProxyCreator.class, AnnotationAwareAspectJAutoProxyCreator.class* 	注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的**/configureAutoProxyCreator(parserContext, element);/*** 2. 解析 <aop:config> 标签的子元素 (pointcut, advisor, aspect)* 解析 <aspect ...>:* 每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor 的BeanDefinition 然后将其注册到 BeanFactory**  AspectJPointcutAdvisor 的包含情况* 每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor(通知器) 类型的BeanDefinition 然后将其注册到 BeanFactory* 	AspectJPointcutAdvisor 内部包含五种通知类类型:  AspectJAfterReturningAdvice AspectJAfterAdvice AspectJAroundAdvice AspectJMethodBeforeAdvice AspectJAfterThrowingAdvice*  而每种通知类型的内部又主要有三个关键属性,包括:*  1. java.lang.reflect.Method(通知切面的方法)*	2. org.springframework.aop.aspectj.AspectJExpressionPointcut(切入点表达式)* 	3. org.springframework.aop.aspectj.AspectInstanceFactory (切面实例工厂)*/List<Element> childElts = DomUtils.getChildElements(element);for (Element elt: childElts) {String localName = parserContext.getDelegate().getLocalName(elt);switch (localName) {/*** 解析 pointcut/切入点  //筛选连接点, 即: 哪些方法需要被代理* 解析为 org.springframework.aop.aspectj.AspectJExpressionPointcut 注册其 BeanDefinition*/case POINTCUT -> parsePointcut(elt, parserContext);/***  解析 advisor/通知/建议/增强处理  //即: 增强功能这一部分代码* 解析为 org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor 注册其 BeanDefinition*/case ADVISOR -> parseAdvisor(elt, parserContext);/***/case ASPECT -> parseAspect(elt, parserContext);}}parserContext.popAndRegisterContainingComponent();return null;}

tx 标签

前文说了由 org.springframework.transaction.config.TxAdviceBeanDefinitionParser 负责XML解析

先来到父类方法解析 TransactionInterceptor

org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal

/*** Creates a {@link BeanDefinitionBuilder} instance for the* {@link #getBeanClass bean Class} and passes it to the* {@link #doParse} strategy method.* @param element the element that is to be parsed into a single BeanDefinition* @param parserContext the object encapsulating the current state of the parsing process* @return the BeanDefinition resulting from the parsing of the supplied {@link Element}* @throws IllegalStateException if the bean {@link Class} returned from* {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}* @see #doParse**/@Overrideprotected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {/**** 1. 解析 <tx:advice ... 标签 封装为`GenericBeanDefinition`* 其名称和class为org.springframework.transaction.interceptor.TransactionInterceptor* 注意这个 TransactionInterceptor 实现了MethodInterceptor相当于是个Advice** org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser* #parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal***/BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();String parentName = getParentName(element);if (parentName != null) {builder.getRawBeanDefinition().setParentName(parentName);}Class<?> beanClass = getBeanClass(element);if (beanClass != null) {builder.getRawBeanDefinition().setBeanClass(beanClass);}else {String beanClassName = getBeanClassName(element);if (beanClassName != null) {builder.getRawBeanDefinition().setBeanClassName(beanClassName);}}builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));BeanDefinition containingBd = parserContext.getContainingBeanDefinition();if (containingBd != null) {// Inner bean definition must receive same scope as containing bean.builder.setScope(containingBd.getScope());}if (parserContext.isDefaultLazyInit()) {// Default-lazy-init applies to custom bean definitions as well.builder.setLazyInit(true);}doParse(element, parserContext, builder);return builder.getBeanDefinition();}

在回来解析到 transactionAttributeSource

org.springframework.transaction.config.TxAdviceBeanDefinitionParser#doParse

@Overrideprotected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {/*** 添加 transactionManager (事务管理)的 ref*/builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);if (txAttributes.size() > 1) {parserContext.getReaderContext().error("Element <attributes> is allowed at most once inside element <advice>", element);}else if (txAttributes.size() == 1) {/*** 解析 <tx:attributes> ...  子标签* 包括: 匹配的目标方法, 事务的传播属性, 是否只读..* 类型是 `org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource`*/// Using attributes source.Element attributeSourceElement = txAttributes.get(0);RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);}else {// Assume annotations source.builder.addPropertyValue("transactionAttributeSource",new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));}}

汇总图

image

事务相关对象的BeanDefinition 对应AOP的三剑客

首先一点事务增强, 本质上就是AOP的逻辑, 在AOP最重要的就是AspectJPointcutAdvisor包含的三剑客
image

对应关系
image

可以看到少了AspectInstanceFactory 在事务这里的逻辑中不需它; 因为切面的逻辑是硬编码在TransactionInterceptor中了;

至此在事务相关的AOP对象已经齐了

三、事务增强 相关的对象创建

internalAutoProxyCreator 对象

首先是 internalAutoProxyCreator 它是负责创建AOP对象 它本身是BPP, 它会在 registerBeanPostProcessors(beanFactory); 过程中被实例化
org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)

/***  in short:*  1. 拿到所有实现 BeanPostProcessor 的 bean, 然后进行分类存起来*  这有一点, Spring 对 BeanDefinition 分成三种角色:*  	1. 用户定义的 Bean (ROLE_APPLICATION)*      2. 较复杂的 (ROLE_SUPPORT) 较复杂的? 通常是一个外部配置*      3. Spring 内置的(ROLE_INFRASTRUCTURE)*  2. 如果实现了 BeanPostProcessor 则会实例化这个bean, 但注意这里只是注册,并不会调用BeanPostProcessor的相关方法**  另外 BeanPostProcessor 粗粒度太大, Spring 还细分一些子接口:*  - SmartInstantiationAwareBeanPostProcessor 它提供了更高级的Bean实例化控制方法。主要作用在于允许对Bean的实例化过程进行更精细的控制和定制。*  - MergedBeanDefinitionPostProcessor 在合并Bean定义(MergedBeanDefinition)之后但在实例化Bean之前,允许对合并后的Bean定义进行修改、调整或附加元数据。*  - DestructionAwareBeanPostProcessor 它允许在Bean被销毁之前(例如,容器关闭或特定作用域的Bean销毁)执行一些操作。*/public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {// WARNING: Although it may appear that the body of this method can be easily// refactored to avoid the use of multiple loops and multiple lists, the use// of multiple lists and multiple passes over the names of processors is// intentional. We must ensure that we honor the contracts for PriorityOrdered// and Ordered processors. Specifically, we must NOT cause processors to be// instantiated (via getBean() invocations) or registered in the ApplicationContext// in the wrong order.//// Before submitting a pull request (PR) to change this method, please review the// list of all declined PRs involving changes to PostProcessorRegistrationDelegate// to ensure that your proposal does not result in a breaking change:// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22/*** 拿到所有实现 BeanPostProcessor 的 bean名称*/String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);// Register BeanPostProcessorChecker that logs an info message when// a bean is created during BeanPostProcessor instantiation, i.e. when// a bean is not eligible for getting processed by all BeanPostProcessors./*** 计算 BeanPostProcessor(BPP) 的总数.* +1 是什么操作? 原因是: 下一行,又加了一个 BeanPostProcessorChecker*/int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;// BeanPostProcessorChecker 这个BeanPostProcessor, 没啥实际作用, 就是记录了一些日志;beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, postProcessorNames, beanProcessorTargetCount));// Separate between BeanPostProcessors that implement PriorityOrdered,// Ordered, and the rest./*** 对 BeanPostProcessor 进行分类存起来,  再调用, 每个集合分别是* 1. priorityOrderedPostProcessors //有实现(PriorityOrdered)排序接口的* 2. internalPostProcessors //Spring内部的bean, 见: Spring将bean分为三种角色* 3. orderedPostProcessorNames //实现 Ordered 接口* 4. nonOrderedPostProcessorNames //没有指定顺序, 无序的*/List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {/*** 注意, 若 bean 实现了 PriorityOrdered 接口, 则会优先实例化它;*/BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// First, register the BeanPostProcessors that implement PriorityOrdered.sortPostProcessors(priorityOrderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);// Next, register the BeanPostProcessors that implement Ordered.List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}sortPostProcessors(orderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, orderedPostProcessors);/*** 注册所有常规BeanPostProcessors* 这里 getBean 实例化bean !;*/// Now, register all regular(常规) BeanPostProcessors.List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String ppName : nonOrderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);nonOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);/*** 最后, 注册所有 内置 BeanPostProcessor*/// Finally, re-register all internal BeanPostProcessors.sortPostProcessors(internalPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, internalPostProcessors);//最后再放一个 ApplicationListenerDetector 让它在最后 (不是重点, 见名应该是事件相关的)// Re-register post-processor for detecting inner beans as ApplicationListeners,// moving it to the end of the processor chain (for picking up proxies etc).beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));}

DefaultBeanFactoryPointcutAdvisor 和 AspectJExpressionPointcut 对象

internalAutoProxyCreator 会在 postProcessBeforeInstantiation 回调中的org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#shouldSkip判断时实例化所有的 Advisor

在实例化 Advisor 填充属性时也会把 AspectJExpressionPointcut 也实例化

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation

@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {/*** 常规情况下:  这里只是检查下缓存和标记缓存* 对于真正的AOP代理创建见:* {@link AbstractAutoProxyCreator#postProcessAfterInitialization(java.lang.Object, java.lang.String)}*/Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {/****  不管需不需要, 只要处理过了就缓存* 	advisedBeans 这个变量, 缓存所有处理过的 bean名称;* 	value 为 boolean值, 如果为false 则不处理*/if (this.advisedBeans.containsKey(cacheKey)) {return null;}/*** isInfrastructureClass 是否是基础功能类,即 AOP相关的几个类:* Advice.class Pointcut.class Advisor.class AopInfrastructureBean.class 都为true** shouldSkip 判断时, 拿到容器的所有 Advisor, 并且实例化 `getBean()` 这个 Advisor*/if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;}

TransactionInterceptort 对象

是在 internalAutoProxyCreator 遇到需要增强代理的对象时, 这里是 bookService 去 getAdvicesAndAdvisorsForBean 查找到其匹配 bookService 的 Advisor
这里若 Advisor 匹配, 会调用 getAdvice 获取其切面, 此时切面不存在则会实例化它

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}/*** 拿到所有匹配织入当前bean的 所有通知器(Advisor)* 做了三件事, 见: {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean(java.lang.Class, java.lang.String, org.springframework.aop.TargetSource)}* 1. 往返回 `AspectJXXXAdvice`列表数组`0`索引 插入一个{@link org.springframework.aop.interceptor.ExposeInvocationInterceptor} 实例* 方便传递参数用的** 2. 怎么匹配(Advisor)?* Advisor中的 `AspectJExpressionPointcut` 是实现 {@link ClassFilter} 和 {@link org.springframework.aop.MethodMatcher} 接口* 一个进行类匹配, 一个进行方法匹配. Advisor 匹配会调用 getAdvice 获取其切面, 此时切面不存在则会实例化** 3.  排序, 基于 `有向无环` 图进行排序; 可能匹配到多个切面(aspect)**/// Create proxy if we have advice.Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {/***{@link org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#advisedBeans}* 这个变量缓存所有处理过的 bean名称, value 为 boolean值, 如果为false 则不处理*/this.advisedBeans.put(cacheKey, Boolean.TRUE);//缓存, 表示已处理/*** 创建代理**/Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}

汇总图

image

四、事务增强 相关的方法调用流程

对于嵌套事务关键两个对象

首先梳理一下, 这里最复杂的问题是有多层方法嵌套的事务时, 对于事务的传播特性, 应该如何处理?
因为整个处理流程中涉及的对象非常多, 对于理解这里面的逻辑最为关键的对象有两个 TransactionStatus 和 TransactionInfo

TransactionInfo

该对象是在事务多层嵌套时流转的核心对象, 它几乎持有事务相关的所有对象
org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo

/*** Opaque object used to hold transaction information. Subclasses* must pass it back to methods on this class, but not see its internals.*/
protected static final class TransactionInfo {// 1. 事务管理器(DataSource/JPA等事务管理器)private final PlatformTransactionManager transactionManager;// 2. 事务属性(传播行为、隔离级别、超时、只读等)private final TransactionAttribute transactionAttribute;// 3. 切入的目标方法名(用于日志、异常追踪)private final String joinpointIdentification;// 4. 真实的事务状态(Spring 事务核心状态对象)private TransactionStatus transactionStatus;// 5. 上一个事务信息(处理事务嵌套/挂起,形成链表)private TransactionInfo previousTransactionInfo;@Nullableprivate TransactionInfo oldTransactionInfo;public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {this.transactionManager = transactionManager;this.transactionAttribute = transactionAttribute;this.joinpointIdentification = joinpointIdentification;}public PlatformTransactionManager getTransactionManager() {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");return this.transactionManager;}@Nullablepublic TransactionAttribute getTransactionAttribute() {return this.transactionAttribute;}/*** Return a String representation of this joinpoint (usually a Method call)* for use in logging.*/public String getJoinpointIdentification() {return this.joinpointIdentification;}public void newTransactionStatus(@Nullable TransactionStatus status) {this.transactionStatus = status;}@Nullablepublic TransactionStatus getTransactionStatus() {return this.transactionStatus;}/*** Return whether a transaction was created by this aspect,* or whether we just have a placeholder to keep ThreadLocal stack integrity.*/public boolean hasTransaction() {return (this.transactionStatus != null);}private void bindToThread() {// Expose current TransactionStatus, preserving any existing TransactionStatus// for restoration after this transaction is complete.this.oldTransactionInfo = transactionInfoHolder.get();transactionInfoHolder.set(this);}private void restoreThreadLocalStatus() {// Use stack to restore old transaction TransactionInfo.// Will be null if none was set.transactionInfoHolder.set(this.oldTransactionInfo);}@Overridepublic String toString() {return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");}
}
// 1. 事务管理器(DataSource/JPA等事务管理器)
private final PlatformTransactionManager transactionManager;// 2. 事务属性(传播行为、隔离级别、超时、只读等)
private final TransactionAttribute transactionAttribute;// 3. 切入的目标方法名(用于日志、异常追踪)
private final String joinpointIdentification;// 4. 真实的事务状态(Spring 事务核心状态对象)
private TransactionStatus transactionStatus;// 5. 上一个事务信息(处理事务嵌套/挂起,形成链表)
private TransactionInfo previousTransactionInfo;@Nullable
private TransactionInfo oldTransactionInfo;

记录了当前线程正在执行的事务所有关键信息,支撑事务的开启、挂起、恢复、提交 / 回滚全流程

对于这个状态对象关键的点是, 在多层嵌套时;
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

  • 在每次嵌套时都会调用 bindToThread
private void bindToThread() {// Expose current TransactionStatus, preserving any existing TransactionStatus// for restoration after this transaction is complete.this.oldTransactionInfo = transactionInfoHolder.get();transactionInfoHolder.set(this);
}

保存上一个 TransactionInfo

  • 在嵌套方法结束返回是调用 restoreThreadLocalStatus
private void restoreThreadLocalStatus() {// Use stack to restore old transaction TransactionInfo.// Will be null if none was set.transactionInfoHolder.set(this.oldTransactionInfo);
}

恢复上一个 TransactionInfo

TransactionStatus

org.springframework.transaction.TransactionStatus

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {/*** Return whether this transaction internally carries a savepoint,* that is, has been created as nested transaction based on a savepoint.* <p>This method is mainly here for diagnostic purposes, alongside* {@link #isNewTransaction()}. For programmatic handling of custom* savepoints, use the operations provided by {@link SavepointManager}.* <p>The default implementation returns {@code false}.* @see #isNewTransaction()* @see #createSavepoint()* @see #rollbackToSavepoint(Object)* @see #releaseSavepoint(Object)*/default boolean hasSavepoint() {return false;}/*** Flush the underlying session to the datastore, if applicable:* for example, all affected Hibernate/JPA sessions.* <p>This is effectively just a hint and may be a no-op if the underlying* transaction manager does not have a flush concept. A flush signal may* get applied to the primary resource or to transaction synchronizations,* depending on the underlying resource.* <p>The default implementation is empty, considering flush as a no-op.*/@Overridedefault void flush() {}}

大部分属性其实都在它的父类 TransactionExecution
org.springframework.transaction.TransactionExecution


package org.springframework.transaction;/*** Common representation of the current state of a transaction.* Serves as base interface for {@link TransactionStatus} as well as* {@link ReactiveTransaction}, and as of 6.1 also as transaction* representation for {@link TransactionExecutionListener}.** @author Juergen Hoeller* @since 5.2*/
public interface TransactionExecution {/*** Return the defined name of the transaction (possibly an empty String).* <p>In case of Spring's declarative transactions, the exposed name will be* the {@code fully-qualified class name + "." + method name} (by default).* <p>The default implementation returns an empty String.* @since 6.1* @see TransactionDefinition#getName()*/default String getTransactionName() {return "";}/*** Return whether there is an actual transaction active: this is meant to cover* a new transaction as well as participation in an existing transaction, only* returning {@code false} when not running in an actual transaction at all.* <p>The default implementation returns {@code true}.* @since 6.1* @see #isNewTransaction()* @see #isNested()* @see #isReadOnly()*/default boolean hasTransaction() {return true;}/*** Return whether the transaction manager considers the present transaction* as new; otherwise participating in an existing transaction, or potentially* not running in an actual transaction in the first place.* <p>This is primarily here for transaction manager state handling.* Prefer the use of {@link #hasTransaction()} for application purposes* since this is usually semantically appropriate.* <p>The "new" status can be transaction manager specific, e.g. returning* {@code true} for an actual nested transaction but potentially {@code false}* for a savepoint-based nested transaction scope if the savepoint management* is explicitly exposed (such as on {@link TransactionStatus}). A combined* check for any kind of nested execution is provided by {@link #isNested()}.* <p>The default implementation returns {@code true}.* @see #hasTransaction()* @see #isNested()* @see TransactionStatus#hasSavepoint()*/default boolean isNewTransaction() {return true;}/*** Return if this transaction executes in a nested fashion within another.* <p>The default implementation returns {@code false}.* @since 6.1* @see #hasTransaction()* @see #isNewTransaction()* @see TransactionDefinition#PROPAGATION_NESTED*/default boolean isNested() {return false;}/*** Return if this transaction is defined as read-only transaction.* <p>The default implementation returns {@code false}.* @since 6.1* @see TransactionDefinition#isReadOnly()*/default boolean isReadOnly() {return false;}/*** Set the transaction rollback-only. This instructs the transaction manager* that the only possible outcome of the transaction may be a rollback, as* alternative to throwing an exception which would in turn trigger a rollback.* <p>The default implementation throws an UnsupportedOperationException.* @see #isRollbackOnly()*/default void setRollbackOnly() {throw new UnsupportedOperationException("setRollbackOnly not supported");}/*** Return whether the transaction has been marked as rollback-only* (either by the application or by the transaction infrastructure).* <p>The default implementation returns {@code false}.* @see #setRollbackOnly()*/default boolean isRollbackOnly() {return false;}/*** Return whether this transaction is completed, that is,* whether it has already been committed or rolled back.* <p>The default implementation returns {@code false}.*/default boolean isCompleted() {return false;}}
// 1. 是否是新事务
boolean isNewTransaction();// 2. 是否标记为 只回滚
boolean isRollbackOnly();// 3. 事务是否已完成(提交/回滚)
boolean isCompleted();// 4. 是否是只读事务
boolean isReadOnly();// 5. 是否被标记为 必须回滚
default boolean isRollbackOnly()

in short 可以理解为一个事务的状态: 是否是新事务, 是否是序列化, 是否是嵌套, 是否是只读的等

对于这个状态对象关键的点是, 在多层嵌套时, 主要在:
org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
视情况(主要是根据事务的传播特性, 该方法也是处理多层事务的核心方法) 创建或修改事务状态中的属性

对于线程绑定事务管理对象 TransactionSynchronizationManager

org.springframework.transaction.support.TransactionSynchronizationManager

public abstract class TransactionSynchronizationManager {/*** 线程私有事务资源*/private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** 事务同步?*/private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");/*** 当前事务的 名称*/private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<>("Current transaction name");/*** 当前事务 是否只读*/private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<>("Current transaction read-only status");/*** 当前事务的 隔离级别*/private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<>("Current transaction isolation level");/*** 事务是否实际激活*/private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<>("Actual transaction active");/*** Retrieve a resource for the given key that is bound to the current thread.* @param key the key to check (usually the resource factory)* @return a value bound to the current thread (usually the active* resource object), or {@code null} if none* @see ResourceTransactionManager#getResourceFactory()*/@Nullablepublic static Object getResource(Object key) {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);return doGetResource(actualKey);}/*** Actually check the value of the resource that is bound for the given key.*/@Nullableprivate static Object doGetResource(Object actualKey) {Map<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.get(actualKey);// Transparently remove ResourceHolder that was marked as void...if (value instanceof ResourceHolder resourceHolder && resourceHolder.isVoid()) {map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {resources.remove();}value = null;}return value;}/*** Bind the given resource for the given key to the current thread.* @param key the key to bind the value to (usually the resource factory)* @param value the value to bind (usually the active resource object)* @throws IllegalStateException if there is already a value bound to the thread* @see ResourceTransactionManager#getResourceFactory()*/public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");Map<Object, Object> map = resources.get();// set ThreadLocal Map if none foundif (map == null) {map = new HashMap<>();resources.set(map);}Object oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder resourceHolder && resourceHolder.isVoid()) {oldValue = null;}if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");}}...........

可以看到有相当多的 ThreadLocal 变量, 它就是负责将事务信息与当前线程绑定的

绑定的源码逻辑
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

/**** 真正在数据库中支持事务,* 1. 从数据源中获取链接* 2. 设置事务的隔离级别* 3. 关闭连接的自动提交* 4. 绑定连接到线程中 TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());** so , 如果想要复用或者是适配 Spring 的事务支持的数据库连接可以这么拿:* ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());**/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {/*** 如果 ConnectionHolder 中没有数据库连接 则从数据源中获取,再存到[事务上下文] txObject对象中* (真正的从数据源获取JDBC连接)*/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)./*** 关闭自动提交, 由 Spring 负责管理了*/if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}/*** 判断是否 需要设置为只读事务*/prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}/*** 如果是新的连接 (也可以理解为新事务)* 绑定到线程中*/// Bind the connection holder to the thread.if (txObject.isNewConnectionHolder()) {/*** 将 ConnectionHolder 绑定到当前线程中 (ThreadLocal)** 如果想要复用或者是适配 Spring 的事务支持可以这么拿:* ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());* 数据库的连接** > ConnectionHolder 可以理解为包装了数据库的连接, 不重要*/TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, obtainDataSource());txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}
}

获取/复用的数据库连接的源码逻辑
org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction

@Overrideprotected Object doGetTransaction() {/*** 1. 创建一个DataSourceTransactionObject  封装对象, 可以理解为数据源上下文, 它包含:* ConnectionHolder 数据库连接Holder对象* newConnectionHolder : 是否是新连接 属性* savepointAllowed: 是否允许'保存点'* readOnly: 是否只读**/DataSourceTransactionObject txObject = new DataSourceTransactionObject();///*** 设置允许'保存点'*/txObject.setSavepointAllowed(isNestedTransactionAllowed());/***  2. 从[事务同步管理器]中获取连接持有器, Holder 持有一个数据库连接, 如果是第一次获取则是 null*  事务同步管理器: (TransactionSynchronizationManager): 该对象管理的资源都是绑定到当前事务中的; 内部使用的 ThreadLocal 存储的*  如果没有事务, 第一次获取则是 null,*  如果之前有事务, 将会获取的同一个 ConnectionHolder*  将获取到的ConnectionHolder 存到 DataSourceTransactionObject**  总之核心逻辑就是复用事务连接*/ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());txObject.setConnectionHolder(conHolder, false);/*** 放到 事务上下文*/return txObject;}

重中之重的是: 的第三方扩展库/框架 都是通过这种方式支持Spring 声明式事务的, 见后文!

关键的源码流程

拦截器主流程入口

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

/*** General delegate for around-advice-based subclasses, delegating to several other template* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}* as well as regular {@link PlatformTransactionManager} implementations and* {@link ReactiveTransactionManager} implementations for reactive return types.* @param method the Method being invoked* @param targetClass the target class that we're invoking the method on* @param invocation the callback to use for proceeding with the target invocation* @return the return value of the method, if any* @throws Throwable propagated from the target invocation*/@Nullableprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {/*** 获取事务属性源* 即: <tx:attributes> 标签配置的*/// If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas = getTransactionAttributeSource();/*** 1. 通过目标方法去匹配, 拿到对应的属性配置(TransactionAttribute), 比如: 是否只读, 事务传播属性, rollbackOn 等*/final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);/*** 2. 拿到对应的事务管理器对象 (bean) TransactionManager*/final TransactionManager tm = determineTransactionManager(txAttr);if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager rtm) {boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);boolean hasSuspendingFlowReturnType = isSuspendingFunction &&COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());if (isSuspendingFunction && !(invocation instanceof CoroutinesInvocationCallback)) {throw new IllegalStateException("Coroutines invocation not supported: " + method);}CoroutinesInvocationCallback corInv = (isSuspendingFunction ? (CoroutinesInvocationCallback) invocation : null);ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {Class<?> reactiveType =(isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);if (adapter == null) {throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +method.getReturnType());}return new ReactiveTransactionSupport(adapter);});InvocationCallback callback = invocation;if (corInv != null) {callback = () -> KotlinDelegate.invokeSuspendingFunction(method, corInv);}return txSupport.invokeWithinTransaction(method, targetClass, callback, txAttr, rtm);}PlatformTransactionManager ptm = asPlatformTransactionManager(tm);/*** 3. 生成获取到事务方法的唯一ID*/final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {/*** 4. 使用 TransactionManager 创建事务, 获取到事务信息对象 (TransactionInfo)*/// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {/*** 5. 以事务执行调用, 回调方法* retVal 是dao方法(原始方法) 执行的返回值*/// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {/*** 6. 执行异常, 执行异常回滚操作*/// target invocation exceptioncompleteTransactionAfterThrowing(txInfo, ex);/*** 注意这里还要再抛出去, 给外层事务知道 内层执行出现异常*/throw ex;}finally {/*** 7. 不管异常与否, 清除当前线程的[事务上下文]信息* 这个是在{@link org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary(org.springframework.transaction.PlatformTransactionManager, org.springframework.transaction.interceptor.TransactionAttribute, java.lang.String)}* 中返回时 prepareTransactionInfo方法中,调用了 `txInfo.newTransactionStatus(status);` 保存到 ThreadLocal 中的*/cleanupTransactionInfo(txInfo);}if (retVal != null && txAttr != null) {TransactionStatus status = txInfo.getTransactionStatus();if (status != null) {if (retVal instanceof Future<?> future && future.isDone()) {try {future.get();}catch (ExecutionException ex) {if (txAttr.rollbackOn(ex.getCause())) {status.setRollbackOnly();}}catch (InterruptedException ex) {Thread.currentThread().interrupt();}}else if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {// Set rollback-only in case of Vavr failure matching our rollback rules...retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);}}}/*** 8. 执行正常,执行正常提交处理*/commitTransactionAfterReturning(txInfo);return retVal;}else {.....}

创建 TransactionInfo

使用 TransactionManager 创建事务, 获取到事务的上下文对象 (TransactionInfo)

org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {/*** 包装了一下事务源属性*/// If no name specified, apply method identification as transaction name.if (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}/*** 获取[事务状态]: 表示* 是否 新事务* 是否 有保存点* 是否 只回滚* 是否 完成*/TransactionStatus status = null;if (txAttr != null) {if (tm != null) {/***  1. 获取到一个封装对象: 可以理解为事务的状态*  * 它包括: 是否只读, 是否允许'保存点', 从[事务同步管理器]获取 ConnectionHolder (注意第一次获取'无事务', Holder的connect是null的 );**  2. 判断当前是否存在事务, 判断依据是 ConnectionHolder 内部 Active 标识符*	 * 如果存在则, 处理事务传播特性*   * {@link org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction(org.springframework.transaction.TransactionDefinition, java.lang.Object, boolean)}**  3. 判断一下当前事务传播特性, 创建新的事务* 	 * 在这里如果是 REQUIRED, REQUIRES_NEW, NESTED 传播特性, 才需要需要创建事务**  4. 开始事务* 	创建一个[事务状态]对象, 如果没有 数据库连接 则从数据源 中获取, 封装为 ConnectionHolder, 并赋值给[事务上下文]对象* 	开始事务, 其实就是设置数据库连接的一些属性:* 	  关闭自动提交, 由Spring 负责管理了* 	  判断是否需要设置为只读事务* 	  如果是新事务, 则绑定到当前线程中 (ThreadLocal)*  org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction(org.springframework.transaction.TransactionDefinition, java.lang.Object, boolean, boolean, org.springframework.transaction.support.AbstractPlatformTransactionManager.SuspendedResourcesHolder)** 见 {@link org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction(org.springframework.transaction.TransactionDefinition)}*/status = tm.getTransaction(txAttr);}else {if (logger.isDebugEnabled()) {logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");}}}/*** 见: {@link org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo(org.springframework.transaction.PlatformTransactionManager, org.springframework.transaction.interceptor.TransactionAttribute, java.lang.String, org.springframework.transaction.TransactionStatus)}*/return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}

获取事务

org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

/*** This implementation handles propagation behavior. Delegates to* {@code doGetTransaction}, {@code isExistingTransaction}* and {@code doBegin}.* @see #doGetTransaction* @see #isExistingTransaction* @see #doBegin*/@Overridepublic final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {/*** 将 TransactionStatus 和 事务方法唯一ID, 事务源属性 封装为 TransactionDefinition*/// Use defaults if no transaction definition given.TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());/*** 1. 获取到一个封装对象(DataSourceTransactionObject): 可以理解为事务上下文* 它包括: 是否只读, 是否允许'保存点', 从[事务同步管理器]获取到的 ConnectionHolder (注意第一次'无事务'获取的 连接持有器Holder的connect是null的 );** [事务同步管理器](TransactionSynchronizationManager): 该对象管理的资源都是绑定到当前事务中的; 内部使用ThreadLocal 存储的* org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction()  设置允许'保存点'*/Object transaction = doGetTransaction();boolean debugEnabled = logger.isDebugEnabled();/*** 2. 判断当前是否存在事务, 判断依据是 ConnectionHolder 内部 Active 标识符* 如果存在则, 处理事务传播特性* org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction(org.springframework.transaction.TransactionDefinition, java.lang.Object, boolean)*/if (isExistingTransaction(transaction)) {/*** 如果当前存在事务, 则走这个分支*/// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(def, transaction, debugEnabled);}// Check definition settings for new transaction.if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());}/*** 3. 判断一下当前事务传播特性, 创建新的事务* 在这里如果是 REQUIRED, REQUIRES_NEW, NESTED 传播特性, 需要创建事务*/// No existing transaction found -> check propagation behavior to find out how to proceed.if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {/*** 挂起事务 (当前没有事务)*/SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);}try {/***  4. 开始事务*   * 创建一个[事务状态]对象, 关键属性: 是否是新的、是否有保存点、是否只读、是否嵌套、是否为只回滚、是否完成。*   * 如果没有 数据库连接 则从数据源 中获取, 封装为 ConnectionHolder, 并赋值给[事务上下文]对象* 	  * 其实就是设置数据库连接的一些属性:* 	  * 关闭自动提交, 由Spring 负责管理了* 	  * 判断是否需要设置为只读事务* 	  * 如果是新事务, 则通过 [事务同步资源管理器] TransactionSynchronizationManager 绑定到当前线程中 (ThreadLocal)*/return startTransaction(def, transaction, false, debugEnabled, suspendedResources);}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}else {// Create "empty" transaction: no actual transaction, but potentially synchronization.if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + def);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);}}
在已有事务的情况-处理事务的传播特性

在已有事务的情况下, 主要是处理事务的传播特性
org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction

/*** Create a TransactionStatus for an existing transaction.*/private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {/*** 注意一点 , 现在是外部有事务的情况* transaction 是txObject*//*** 1.如果是 NEVER (不允许事务) 传播特性, 则抛出异常*/if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}/*** 2.如果是 NOT_SUPPORTED (不支持当前事务;总是以非事务的方式执行), 传播特性 挂起事务*/if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {if (debugEnabled) {logger.debug("Suspending current transaction");}//挂起事务Object suspendedResources = suspend(transaction);boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);// 返回一个 无事务状态(TransactionStatus)return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}/*** 3.如果是 REQUIRES_NEW 传播特性, 挂起当前事务* 注意该特性:* - 事务挂起后, 新的事务是完全独立的, 包括连接等等, 每个事务独立提交* - 它的内层事务影响外层事务, 但是外层异常不影响内层事务*/if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {if (debugEnabled) {logger.debug("Suspending current transaction, creating new transaction with name [" +definition.getName() + "]");}/*** 挂起当前事务, 何为挂起?* 如果有事务的话*  1. 将 DataSourceTransactionObject [事务上下文]中的 ConnectionHolder 设置为null* 	2. 从[事务同步管理器]中解除绑定: TransactionSynchronizationManager.unbindResource(obtainDataSource())* 	  这样就拿不到对象,不会对事务资源进行复用* 	3. 挂起了, 下次还有恢复, 所以要保存被挂起的事务信息 (SuspendedResourcesHolder)**/SuspendedResourcesHolder suspendedResources = suspend(transaction);try {/***  //开启新的事务*/return startTransaction(definition, transaction, false, debugEnabled, suspendedResources);}catch (RuntimeException | Error beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}}/*** 4.如果是 NESTED (总是以嵌套的方式执行, 可以无限嵌套, MySQL的实现是 基于事务的保存点 savepoint )传播特性*/if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {if (!isNestedTransactionAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " +"specify 'nestedTransactionAllowed' property with value 'true'");}if (debugEnabled) {logger.debug("Creating nested transaction with name [" + definition.getName() + "]");}/*** 使用保存点, 对于NESTED的事务 (空实现,恒等于true)*/if (useSavepointForNestedTransaction()) {/*** 4.1 创建一个新的[事务状态] DefaultTransactionStatus*/// Create savepoint within existing Spring-managed transaction,// through the SavepointManager API implemented by TransactionStatus.// Usually uses JDBC savepoints. Never activates Spring synchronization.DefaultTransactionStatus status = newTransactionStatus(definition, transaction, false, false, true, debugEnabled, null);this.transactionExecutionListeners.forEach(listener -> listener.beforeBegin(status));try {/*** 4.2 创建保存点* 最终是使用 ConnectionHolder 进行创建保存点, 底层是调用JDBC的接口方法 java.sql.Connection#setSavepoint(java.lang.String)* org.springframework.jdbc.datasource.ConnectionHolder#createSavepoint()*/status.createAndHoldSavepoint();}catch (RuntimeException | Error ex) {this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, ex));throw ex;}this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, null));return status;}else {// Nested transaction through nested begin and commit/rollback calls.// Usually only for JTA: Spring synchronization might get activated here// in case of a pre-existing JTA transaction.return startTransaction(definition, transaction, true, debugEnabled, null);}}// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.if (debugEnabled) {logger.debug("Participating in existing transaction");}if (isValidateExistingTransaction()) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {throw new IllegalTransactionStateException("Participating transaction with definition [" +definition + "] specifies isolation level which is incompatible with existing transaction: " +(currentIsolationLevel != null ?DefaultTransactionDefinition.getIsolationLevelName(currentIsolationLevel) :"(unknown)"));}}if (!definition.isReadOnly()) {if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {throw new IllegalTransactionStateException("Participating transaction with definition [" +definition + "] is not marked as read-only but existing transaction is");}}}boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);}
无事务的情况-新建事务

org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction

/*** Start a new transaction.*/
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,boolean nested, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);/*** 1. 创建一个[事务状态]对象* 包括事务: 是否是新的、是否有保存点、是否只读、是否嵌套、是否为只回滚、是否完成。*/DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, nested, debugEnabled, suspendedResources);this.transactionExecutionListeners.forEach(listener -> listener.beforeBegin(status));try {/*** 2. 如果没有 数据库连接 则从数据源 中获取, 封装为 ConnectionHolder, 并赋值给[事务上下文]对象* 设置数据库连的一些属性:*  关闭自动提交, 由Spring 负责管理了*  判断是否需要设置为只读事务*  如果是新事务, 则绑定到当前线程中 (ThreadLocal)**  {@link org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin(java.lang.Object, org.springframework.transaction.TransactionDefinition)}*/doBegin(transaction, definition);}catch (RuntimeException | Error ex) {this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, ex));throw ex;}prepareSynchronization(status, definition);this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, null));return status;
}

事务方法执行异常情况

org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing

/*** Handle a throwable, completing the transaction.* We may commit or roll back, depending on the configuration.* @param txInfo information about the current transaction* @param ex throwable encountered*/protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +"] after exception: " + ex);}/*** 1. 匹配回滚的异常*  txInfo.transactionAttribute.rollbackOn(ex) 匹配回滚的异常*/if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {try {/*** 2. 设置回滚状态*/txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by rollback exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}else {// We don't roll back on this exception.// Will still roll back if TransactionStatus.isRollbackOnly() is true.try {txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by commit exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by commit exception", ex);throw ex2;}}}}

事务方法执行正常情况

org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning

	/*** Execute after successful completion of call, but not after an exception was handled.* Do nothing if we didn't create a transaction.* @param txInfo information about the current transaction*/protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");}/*** 获取到当前的 TransactionStatus 提交到 TransactionManager* 注意并不一定是数据库提交, 取决与 TransactionStatus 的状态实现, 有可能是回滚, 空提交*/txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}

嵌套恢复线程中 TransactionInfo 信息

这个是在{@link org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary(org.springframework.transaction.PlatformTransactionManager, org.springframework.transaction.interceptor.TransactionAttribute, java.lang.String)}
中返回时 prepareTransactionInfo方法中,调用了 txInfo.newTransactionStatus(status); 保存到 ThreadLocal 中的

/*** Reset the TransactionInfo ThreadLocal.* <p>Call this in all cases: exception or normal return!* @param txInfo information about the current transaction (may be {@code null})*/
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {if (txInfo != null) {txInfo.restoreThreadLocalStatus();}
}

调用流程汇总图

image
https://img2024.cnblogs.com/blog/3723410/202603/3723410-20260323222541707-574711567.png

底层处理事务的源码

事务的开启

如果ConnectionHolder 中没有数据库连接 则从数据源中获取, (真正的从数据源获取JDBC连接), 关闭自动提交,绑定到线程
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

/**** 真正在数据库中支持事务,* 1. 从数据源中获取链接* 2. 设置事务的隔离级别* 3. 关闭连接的自动提交* 4. 绑定连接到线程中 TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());* so , 如果想要复用或者是适配 Spring 的事务支持的数据库连接可以这么拿:* ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());**/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {/*** 如果ConnectionHolder 中没有数据库连接 则从数据源中获取,再存到[事务上下文]对象中* (真正的从数据源获取JDBC连接)*/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)./*** 关闭自动提交, 由 Spring 负责管理了*/if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}/*** 判断是否 需要设置为只读事务*/prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}/*** 如果是新的连接 (也可以理解为新事务)*/// Bind the connection holder to the thread.if (txObject.isNewConnectionHolder()) {/**  * 将 ConnectionHolder 绑定到当前线程中 (ThreadLocal)  * 下次再拿的话可以这么拿:  * ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); * * > 可以理解 ConnectionHolder 包装了数据库的连接  */TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, obtainDataSource());txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}
}

事务的提交

判断一下 TransactionStatus 中状态, 决定是否要回滚
org.springframework.transaction.support.AbstractPlatformTransactionManager#commit


/*** This implementation of commit handles participating in existing* transactions and programmatic rollback requests.* Delegates to {@code isRollbackOnly}, {@code doCommit}* and {@code rollback}.* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()* @see #doCommit* @see #rollback*/
@Override
public final void commit(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;/*** 1. 判断 [事务状态] 是否设置了回滚*/if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}processRollback(defStatus, false);return;}/*** 2. 判断 [事务状态] 是否设置了设置了全局回滚* 这里会拿到 ConnectHolder 判断其回滚标记*/if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");}processRollback(defStatus, true);return;}/*** 3. 处理提交*/processCommit(defStatus);
}

触发一些钩子, 只有是 最外层事务才真正的JDBC提交
org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit

private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;boolean commitListenerInvoked = false;try {boolean unexpectedRollback = false;//预留 hook方法prepareForCommit(status);triggerBeforeCommit(status);triggerBeforeCompletion(status);beforeCompletionInvoked = true;if (status.hasSavepoint()) {/*** 1. 是否有事务保存点 (NESTED 才会有保存点)* 有就释放保存点*/if (status.isDebug()) {logger.debug("Releasing transaction savepoint");}unexpectedRollback = status.isGlobalRollbackOnly();this.transactionExecutionListeners.forEach(listener -> listener.beforeCommit(status));commitListenerInvoked = true;status.releaseHeldSavepoint();}else if (status.isNewTransaction()) {/*** 2. 是否是新事务, 即 是最外层事务才真正的JDBC提交*/if (status.isDebug()) {logger.debug("Initiating transaction commit");}unexpectedRollback = status.isGlobalRollbackOnly();this.transactionExecutionListeners.forEach(listener -> listener.beforeCommit(status));commitListenerInvoked = true;/*** 真正的JDBC提交*/doCommit(status);}......

真正的 JDBC层提交
org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit

@Override  
protected void doCommit(DefaultTransactionStatus status) {  DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();  Connection con = txObject.getConnectionHolder().getConnection();  if (status.isDebug()) {  logger.debug("Committing JDBC transaction on Connection [" + con + "]");  }    try {  con.commit();  }    catch (SQLException ex) {  throw translateException("JDBC commit", ex);  }}

事务的回滚

跟提交同理, 只有最外层的才应该真正的提交
org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

/*** Process an actual rollback.* The completed flag has already been checked.* @param status object representing the transaction* @throws TransactionException in case of rollback failure*/private void processRollback(DefaultTransactionStatus status, boolean unexpected) {try {boolean unexpectedRollback = unexpected;boolean rollbackListenerInvoked = false;try {/*** 1. 如果事务状态有保存点, 将事务状态回滚到设置的保存点 (仅NESTED才会存在保存点)*/triggerBeforeCompletion(status);if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Rolling back transaction to savepoint");}this.transactionExecutionListeners.forEach(listener -> listener.beforeRollback(status));rollbackListenerInvoked = true;status.rollbackToHeldSavepoint();}else if (status.isNewTransaction()) {/*** 2. 如果是新(外层)事务的话, 真正执行回滚 (只有在最外层是执行回滚的)*/if (status.isDebug()) {logger.debug("Initiating transaction rollback");}this.transactionExecutionListeners.forEach(listener -> listener.beforeRollback(status));rollbackListenerInvoked = true;/*** 真正在数据库连接层 执行事务回滚*/doRollback(status);}else {// Participating in larger transactionif (status.hasTransaction()) {if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {if (status.isDebug()) {logger.debug("Participating transaction failed - marking existing transaction as rollback-only");}/***  3. 设置 ConnectionHolder 中的 RollbackOnly 标记状态*/doSetRollbackOnly(status);}else {if (status.isDebug()) {logger.debug("Participating transaction failed - letting transaction originator decide on rollback");}}}else {logger.debug("Should roll back transaction but cannot - no transaction available");}// Unexpected rollback only matters here if we're asked to fail earlyif (!isFailEarlyOnGlobalRollbackOnly()) {/*** 4. 设置非预期的回滚标记*/unexpectedRollback = false;}}}catch (RuntimeException | Error ex) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);if (rollbackListenerInvoked) {this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, ex));}throw ex;}triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);if (rollbackListenerInvoked) {this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, null));}// Raise UnexpectedRollbackException if we had a global rollback-only markerif (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}}finally {/*** 5. 清除当前事务信息, 注意, 这里如果有被挂起的事务还会恢复它*/cleanupAfterCompletion(status);}}

真正的 JDBC层回滚
org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback

	@Overrideprotected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();if (status.isDebug()) {logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");}try {con.rollback();}catch (SQLException ex) {throw translateException("JDBC rollback", ex);}}

最后, 看看第三方框架是如何支持Spring声明式事务的

JdbcTemplate 是怎么支持Spring声明式事务的?

org.springframework.jdbc.core.JdbcTemplate
关键在于确保数据源中获取的 JDBC连接 都是同一个, 而在声明式事务配置中, 无论是 dataSource 对象, 还是 jdbcTemplate Bean 都没有被代理; 从配置上, 来看也完全没有关联

其实是 JdbcTemplate 在获取连接时 适配了 Spring 事务相关的代码, 关键源码:

  • org.springframework.jdbc.core.JdbcTemplate#execute(org.springframework.jdbc.core.CallableStatementCreator, org.springframework.jdbc.core.CallableStatementCallback<T>)
  • org.springframework.jdbc.datasource.DataSourceUtils#getConnection
  • org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
public static Connection doGetConnection(DataSource dataSource) throws SQLException {  Assert.notNull(dataSource, "No DataSource specified");  //从 TransactionSynchronizationManager 获取连接 (由Spring声明式事务处理的连接)ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  conHolder.requested();  if (!conHolder.hasConnection()) {  logger.debug("Fetching resumed JDBC Connection from DataSource");  conHolder.setConnection(fetchConnection(dataSource));  }  return conHolder.getConnection();  }  // Else we either got no holder or an empty thread-bound holder here.  logger.debug("Fetching JDBC Connection from DataSource");  Connection con = fetchConnection(dataSource);
......

MyBatis 是怎么支持Spring声明式事务的?

org.mybatis.spring.transaction.SpringManagedTransaction

org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

/*** Actually obtain a JDBC Connection from the given DataSource.* Same as {@link #getConnection}, but throwing the original SQLException.* <p>Is aware of a corresponding Connection bound to the current thread, for example* when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread* if transaction synchronization is active (for example, if in a JTA transaction).* <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.* @param dataSource the DataSource to obtain Connections from* @return a JDBC Connection from the given DataSource* @throws SQLException if thrown by JDBC methods* @see #doReleaseConnection*/public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {conHolder.requested();if (!conHolder.hasConnection()) {logger.debug("Fetching resumed JDBC Connection from DataSource");conHolder.setConnection(fetchConnection(dataSource));}return conHolder.getConnection();}// Else we either got no holder or an empty thread-bound holder here.logger.debug("Fetching JDBC Connection from DataSource");Connection con = fetchConnection(dataSource);if (TransactionSynchronizationManager.isSynchronizationActive()) {try {// Use same Connection for further JDBC actions within the transaction.// Thread-bound object will get removed by synchronization at transaction completion.ConnectionHolder holderToUse = conHolder;if (holderToUse == null) {holderToUse = new ConnectionHolder(con);}else {holderToUse.setConnection(con);}holderToUse.requested();TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));holderToUse.setSynchronizedWithTransaction(true);if (holderToUse != conHolder) {TransactionSynchronizationManager.bindResource(dataSource, holderToUse);}}catch (RuntimeException ex) {// Unexpected exception from external delegation call -> close Connection and rethrow.releaseConnection(con, dataSource);throw ex;}}return con;}

Hibernate 是怎么支持Spring声明式事务的?

  1. 开启事务的源码
@Overrideprotected void doBegin(Object transaction, TransactionDefinition definition) {HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {throw new IllegalTransactionStateException("Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +"running within DataSourceTransactionManager if told to manage the DataSource itself. " +"It is recommended to use a single HibernateTransactionManager for all transactions " +"on a single DataSource, no matter whether Hibernate or JDBC access.");}SessionImplementor session = null;try {if (!txObject.hasSessionHolder() || txObject.getSessionHolder().isSynchronizedWithTransaction()) {Interceptor entityInterceptor = getEntityInterceptor();Session newSession = (entityInterceptor != null ?obtainSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :obtainSessionFactory().openSession());if (this.sessionInitializer != null) {this.sessionInitializer.accept(newSession);}if (logger.isDebugEnabled()) {logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");}txObject.setSession(newSession);}session = txObject.getSessionHolder().getSession().unwrap(SessionImplementor.class);boolean holdabilityNeeded = this.allowResultAccessAfterCompletion && !txObject.isNewSession();boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT);if (holdabilityNeeded || isolationLevelNeeded || definition.isReadOnly()) {if (this.prepareConnection && ConnectionReleaseMode.ON_CLOSE.equals(session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode().getReleaseMode())) {// We're allowed to change the transaction settings of the JDBC Connection.if (logger.isDebugEnabled()) {logger.debug("Preparing JDBC Connection of Hibernate Session [" + session + "]");}Connection con = session.connection();Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);txObject.setReadOnly(definition.isReadOnly());if (this.allowResultAccessAfterCompletion && !txObject.isNewSession()) {int currentHoldability = con.getHoldability();if (currentHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {txObject.setPreviousHoldability(currentHoldability);con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);}}txObject.connectionPrepared();}else {// Not allowed to change the transaction settings of the JDBC Connection.if (isolationLevelNeeded) {// We should set a specific isolation level but are not allowed to...throw new InvalidIsolationLevelException("HibernateTransactionManager is not allowed to support custom isolation levels: " +"make sure that its 'prepareConnection' flag is on (the default) and that the " +"Hibernate connection release mode is set to ON_CLOSE.");}if (logger.isDebugEnabled()) {logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]");}}}if (definition.isReadOnly() && txObject.isNewSession()) {// Just set to MANUAL in case of a new Session for this transaction.session.setHibernateFlushMode(FlushMode.MANUAL);// As of 5.1, we're also setting Hibernate's read-only entity mode by default.session.setDefaultReadOnly(true);}if (!definition.isReadOnly() && !txObject.isNewSession()) {// We need AUTO or COMMIT for a non-read-only transaction.FlushMode flushMode = session.getHibernateFlushMode();if (FlushMode.MANUAL.equals(flushMode)) {session.setHibernateFlushMode(FlushMode.AUTO);txObject.getSessionHolder().setPreviousFlushMode(flushMode);}}Transaction hibTx;// Register transaction timeout.int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+// Applies to all statements, also to inserts, updates and deletes!hibTx = session.getTransaction();hibTx.setTimeout(timeout);hibTx.begin();}else {// Open a plain Hibernate transaction without specified timeout.hibTx = session.beginTransaction();}// Add the Hibernate transaction to the session holder.txObject.getSessionHolder().setTransaction(hibTx);// Register the Hibernate Session's JDBC Connection for the DataSource, if set.if (getDataSource() != null) {ConnectionHolder conHolder = new ConnectionHolder(session::connection);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {conHolder.setTimeoutInSeconds(timeout);}if (logger.isDebugEnabled()) {logger.debug("Exposing Hibernate transaction as JDBC [" + conHolder.getConnectionHandle() + "]");}TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);txObject.setConnectionHolder(conHolder);}// 绑定到线程// Bind the session holder to the thread.if (txObject.isNewSessionHolder()) {TransactionSynchronizationManager.bindResource(obtainSessionFactory(), txObject.getSessionHolder());}txObject.getSessionHolder().setSynchronizedWithTransaction(true);}catch (Throwable ex) {if (txObject.isNewSession()) {try {if (session != null && session.getTransaction().getStatus() == TransactionStatus.ACTIVE) {session.getTransaction().rollback();}}catch (Throwable ex2) {logger.debug("Could not rollback Session after failed transaction begin", ex);}finally {SessionFactoryUtils.closeSession(session);txObject.setSessionHolder(null);}}throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);}}
  1. 复用的源码
    org.springframework.orm.hibernate5.SpringSessionContext#currentSession
/*** Retrieve the Spring-managed Session for the current thread, if any.*/@Overridepublic Session currentSession() throws HibernateException {Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);if (value instanceof Session) {return (Session) value;}else if (value instanceof SessionHolder) {// HibernateTransactionManagerSessionHolder sessionHolder = (SessionHolder) value;Session session = sessionHolder.getSession();if (!sessionHolder.isSynchronizedWithTransaction() &&TransactionSynchronizationManager.isSynchronizationActive()) {TransactionSynchronizationManager.registerSynchronization(new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));sessionHolder.setSynchronizedWithTransaction(true);// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session// with FlushMode.MANUAL, which needs to allow flushing within the transaction.FlushMode flushMode = session.getHibernateFlushMode();if (flushMode.equals(FlushMode.MANUAL) &&!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {session.setHibernateFlushMode(FlushMode.AUTO);sessionHolder.setPreviousFlushMode(flushMode);}}return session;}else if (value instanceof EntityManagerHolder) {// JpaTransactionManagerreturn ((EntityManagerHolder) value).getEntityManager().unwrap(Session.class);}if (this.transactionManager != null && this.jtaSessionContext != null) {try {if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {Session session = this.jtaSessionContext.currentSession();if (TransactionSynchronizationManager.isSynchronizationActive()) {TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));}return session;}}catch (SystemException ex) {throw new HibernateException("JTA TransactionManager found but status check failed", ex);}}if (TransactionSynchronizationManager.isSynchronizationActive()) {Session session = this.sessionFactory.openSession();if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {session.setHibernateFlushMode(FlushMode.MANUAL);}SessionHolder sessionHolder = new SessionHolder(session);TransactionSynchronizationManager.registerSynchronization(new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);sessionHolder.setSynchronizedWithTransaction(true);return session;}else {throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");}}
http://www.jsqmd.com/news/524728/

相关文章:

  • Ubuntu系统崩溃排查指南:深入解析关键日志文件
  • 别再手动改配置了!用PowerCLI批量管理ESXi主机NTP设置
  • 工业去离子水采购品牌指南:去离子水批发/工业去离子水采购/工业脱盐水/工业超纯水价格/工业超纯水批发/工业软水/选择指南 - 优质品牌商家
  • 保姆级教程:在Ubuntu 22.04上为ARM板卡交叉编译wireless_tools 29(附补丁和Makefile修改)
  • 你的论文是“人写的”吗?百考通AIGC检测工具,让AI生成内容无所遁形
  • Java音频处理实战:从DFT到FFT的算法实现与频谱可视化
  • 基于springboot特产销售购物平台设计与开发(源码+精品论文+答辩PPT等资料)
  • 告别环境配置烦恼:5分钟用Docker在Linux上跑起人大金仓V9数据库
  • 从零实现PUMA560机械臂运动学正解:基于改进DH建模的Matlab实战解析
  • 视觉提示工程新范式:用SAM模型实现5分钟精准图像分割(附Colab教程)
  • 2026年 三菱GOT触摸屏厂家推荐排行榜:GOT3000/GOT2000/GOT16/GOT15/GOT12/GOT11/GOT10/GS系列工业设备触摸屏品牌深度解析 - 品牌企业推荐师(官方)
  • ESP32-S3 AT指令避坑指南:如何优化HTTP图片上传速度(实测16kb/s提升技巧)
  • ESP8266玩转LED:从硬件连接到代码调试的完整指南(附常见问题排查)
  • 跟我学UDS(ISO14229) ———— NRC码实战解析与避坑指南
  • 告别等待!用vLLM的AsyncLLM引擎实现实时AI对话流式输出(Python异步编程实战)
  • LaTeX绘制点云处理神经网络架构图:从TikZ基础到高级技巧
  • 实战指南:基于Keil MDK的华大HC32F460 DDL库工程搭建全解析
  • 避坑指南:Maya polyToCurve命令的5个隐藏限制及替代方案
  • 为什么树叶在红外图像里总比杯子‘冷‘?一文搞懂材料发射率的视觉骗局
  • 用Grover算法实战优化电商推荐系统:量子计算在NISQ时代的真实案例
  • 基于ECMS控制策略的燃料电池能量管理仿真文件
  • 保姆级教程:在PX4飞控上为你的机器人底盘编写第一个CAN控制程序
  • 【收藏级实战】一周搞定研发平台 Agent 接入!TQL 专属 Agent 开发全攻略(附源码思路)
  • 不用ViewModelLocator?Prism自动绑定还能这样玩(实战演示)
  • 华为手机芯片进化史:从麒麟955到麒麟9000,性能提升有多大?
  • 基于改进Unet的多场景水果图像分割与分类研究
  • OpenCV图像处理实战:5个高频算子解决90%的日常需求
  • 从零搭建FPGA图像处理系统:SDI转HDMI/MIPI全流程解析(基于RK3588平台)
  • 工业控制新突破:用DNNs-MPC搞定非线性大时滞系统(附Python代码示例)
  • 用AI教材生成工具,告别高查重,轻松打造低查重教材!