Spring事务源码解析
Spring的事务属于逻辑事务。不是物理事务。
Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JDBC或者JTA等持久化机制所提供的相关平台框架的事务来实现。例如JDBC的事物管理器就是DataSourceTransactionManager。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager:
public interface PlatformTransactionManager {TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException;
}
通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型。
Spring Boot 使用事务非常简单,需要@EnableTransactionManagement和@Transactional配合使用。首先使用注解@EnableTransactionManagement 开启事务支持后,然后在Service方法上添加注解@Transactional便可。@EnableTransactionManagement,启注解事务管理等同于xml配置方式的 <tx:annotation-driven />。
首先是EnableTransactionManagement类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
这里会import TransactionManagementConfigurationSelector类:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {@Overrideprotected String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) {case PROXY:// 默认就是 PROXYreturn new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};case ASPECTJ:return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};default:return null;}}
}
selectImports会返回两个类:AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration。
-
AutoProxyRegistrar的作用就是向BeanFactory注册InfrastructureAdvisorAutoProxyCreator.class。而InfrastructureAdvisorAutoProxyCreator继承自AbstractAdvisorAutoProxyCreator,就是让容器支持了AOP。 -
ProxyTransactionManagementConfiguration会加载关键的几个bean:
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource());advisor.setAdvice(transactionInterceptor());advisor.setOrder(this.enableTx.<Integer>getNumber("order"));return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor() {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource());if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}}
- BeanFactoryTransactionAttributeSourceAdvisor:实现了 PointcutAdvisor 接口,组合了TransactionAttributeSourcePointcut。
- AnnotationTransactionAttributeSource:解析事务类,得到事务配置相关信息;
- TransactionInterceptor:事务拦截器,实现了 Advice、MethodInterceptor 接口。TransactionInterceptor是个环绕增强,在目标方法执行前开启事务,执行完目标方法后,进行事务提交或者回滚;
事务代理类的创建
了解Spring AOP应该会熟悉这段代码:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}}
在前面 BeanFactoryTransactionAttributeSourceAdvisor 类,该类实现了 PointcutAdvisor 接口,其中的切面 pointcut 便是通过 TransactionAttributeSourcePointcut 来实现的。
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {private TransactionAttributeSource transactionAttributeSource;private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {@Overrideprotected TransactionAttributeSource getTransactionAttributeSource() {return transactionAttributeSource;}};
}
调用路径会到TransactionAttributeSourcePointcut的matches方法,该方法根据能否可以从目标 bean 中得到 TransactionAttribute 来判断是否匹配的。
public boolean matches(Method method, @Nullable Class<?> targetClass) {if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {return false;}TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
上面的tas即AnnotationTransactionAttributeSource。AnnotationTransactionAttributeSource父类AbstractFallbackTransactionAttributeSource实现了getTransactionAttribute方法:
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// First, see if we have a cached value.Object cacheKey = getCacheKey(method, targetClass);Object cached = this.attributeCache.get(cacheKey);if (cached != null) {// Value will either be canonical value indicating there is no transaction attribute,// or an actual transaction attribute.if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return (TransactionAttribute) cached;}}else {// We need to work it out.//重点,获取事务属性TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// Put it in the cache.if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}if (logger.isDebugEnabled()) {logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);}this.attributeCache.put(cacheKey, txAttr);}return txAttr;}
}
再看看computeTransactionAttribute方法:
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// First try is the method in the target class.//先查找目标方式是否有事务属性TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// Second try is the transaction attribute on the target class.//再查找目标类是否有事务属性txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// Fallback is to look at the original method.txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// Last fallback is the class of the original method.txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;
}
从上面可知,方法级别上的注解会覆盖类级别上的注解,两个findTransactionAttribute方法都在AnnotationTransactionAttributeSource实现,
protected TransactionAttribute findTransactionAttribute(Method method) {return determineTransactionAttribute(method);
}protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {return determineTransactionAttribute(clazz);
}
再看看determineTransactionAttribute方法:
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {for (TransactionAnnotationParser annotationParser : this.annotationParsers) {TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);if (attr != null) {return attr;}}return null;
}
annotationParsers集合包含了3个类,分别是SpringTransactionAnnotationParser、JtaTransactionAnnotationParser、Ejb3TransactionAnnotationParser。分别解析不同包的注解,这里的解析类是SpringTransactionAnnotationParser,其parseTransactionAnnotation方法:
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(ae, Transactional.class, false, false);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}
}
以上代码讲解析目标元素上@Transactional注解的相关信息,然后封装成AnnotationAttributes类,其继承LinkedHashMap。
看看parseTransactionAnnotation方法:
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();Propagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());Isolation isolation = attributes.getEnum("isolation");rbta.setIsolationLevel(isolation.value());rbta.setTimeout(attributes.getNumber("timeout").intValue());rbta.setReadOnly(attributes.getBoolean("readOnly"));rbta.setQualifier(attributes.getString("value"));ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();Class<?>[] rbf = attributes.getClassArray("rollbackFor");for (Class<?> rbRule : rbf) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}String[] rbfc = attributes.getStringArray("rollbackForClassName");for (String rbRule : rbfc) {RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);rollBackRules.add(rule);}Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");for (Class<?> rbRule : nrbf) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}String[] nrbfc = attributes.getStringArray("noRollbackForClassName");for (String rbRule : nrbfc) {NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);rollBackRules.add(rule);}rbta.getRollbackRules().addAll(rollBackRules);return rbta;}
切面实现
TransactionInterceptor 实现了方法拦截器 MethodInterceptor 接口,用于对业务类进行事务增强。TransactionInterceptor 的 invoke 方法主要是调用了父类 TransactionAspectSupport 的 invokeWithinTransaction 方法。
@Overridepublic Object invoke(final MethodInvocation invocation) throws Throwable {Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}});}protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable {// 获取事务配置信息final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);// 获取事务管理器final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);// 同步操作if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// 创建事务类TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 环绕增强:触发链条上的下一个拦截器,最终会调用目标类retVal = invocation.proceedWithInvocation();} catch (Throwable ex) {// 出现异常则回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;} finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;} else {// 省略异步操作,原理类似}}
invoke 方法里主要做以下几件事:
- 获取事务属性;
- 获取事务管理器;
- 创建事务;
- 执行目标方法;
- 遇到异常则回滚,正常结束则提交。
创建事务的是TransactionAspectSupport#createTransactionIfNecessary()方法,该方法的调用代码比较多,主要摘取比较重要的看一下:
---+TransactionAspectSupport:createTransactionIfNecessary();`---+AbstractPlatformTransactionManager:getTransaction();+---AbstractPlatformTransactionManager:doGetTransaction();`---+AbstractPlatformTransactionManager:doBegin();+---DataSource:getConnection();`---TransactionSynchronizationManager.bindResource();
参考:
https://blog.csdn.net/wang704987562/article/details/88913808
https://blog.csdn.net/ai_xiangjuan/article/details/79687560
https://blog.csdn.net/u012562943/article/details/78333153
https://www.coder4.com/archives/5917
https://blog.csdn.net/dslztx/article/details/46636079
相关文章:
Spring事务源码解析
Spring的事务属于逻辑事务。不是物理事务。 Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JDBC或者JTA等持久化机制所提供的相关平台框架的事务来实现。例如JDBC的事物管理器就是DataSourceTransactionManager。 Spr…...
71.Spring和SpringMVC为什么需要父子容器?
71.Spring和SpringMVC为什么需要父子容器? 就功能性来说不用子父容器也可以完成(参考:SpringBoot就没用子父容器) 1、所以父子容器的主要作用应该是划分框架边界。有点单一职责的味道。service、dao层我们一般使用spring框架 来…...
标准库 STM32+EC11编码器+I2C ssd1306多级菜单例程
标准库 STM32EC11编码器I2C ssd1306多级菜单例程 📌原创项目来源于:https://github.com/AdamLoong/Embedded_Menu_Simple📍相关功能演示观看:https://space.bilibili.com/74495335 单片机多级菜单v1.2 👉本次采用的是原…...
通过 ChatGPT 的 Function Call 查询数据库
用 Function Calling 的方式实现手机流量包智能客服的例子。 def get_sql_completion(messages, model"gpt-3.5-turbo"):response client.chat.completions.create(modelmodel,messagesmessages,temperature0,tools[{ # 摘自 OpenAI 官方示例 https://github.com/…...
LLM(大语言模型)——大模型简介
目录 概述 发展历程 大语言模型的概念 LLM的应用和影响 大模型的能力、特点 大模型的能力 涌现能力(energent abilities) 作为基座模型支持多元应用的能力 支持对话作为统一入口的能力 大模型的特点 常见大模型 闭源LLM(未公开源…...
SQLserver2008 r2 下载安装配置、使用、新建登录用户及通过Navicat远程连接
目录 一、下载 二、安装配置 1.安装 2.许可条款 3.安装程序支持文件 4.功能选择 5.实例配置 6.服务器配置 7.数据库引擎配置 8.Reporting Services 配置 9.安装进度 编辑 10.完成 三、使用 四、新建登录用户 1.新建登录名 2.常规 3.服务器角色 4. 用户映…...
linux code server 网页版的vscode
下载code-server安装包 挑选一个版本的包 https://github.com/coder/code-server/releases 找个amd64.deb包 wget http://…code-server_4.21.0-rc.1_amd64.deb 系统安装deb包 dpkg -i code-server_4.21.0-rc.1_amd64.deb配置外网访问与密码 可以先运行一下code-server&am…...
【leetcode100-086到090】【动态规划】一维五题合集2
【单词拆分】 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 思路: 首先,我…...
关闭Ubuntu 默认开启的自动安全更新
简介 Ubuntu 和 Debian 应该从很早版本开始预装unattended-upgrades 包,并开启自动更新有安全问题的软件包。 (最小化安装不会安装此包) 有什么影响? 如果软件包有安全漏洞,Ubuntu发布更新包后会自动安装更新并重启…...
python统计文本 2022年9月青少年电子学会等级考试 中小学生python编程等级考试二级真题答案解析
目录 python统计文本 一、题目要求 1、编程实现 2、输入输出...
HomeAssistant系统添加HACS插件商店与远程控制家中智能家居
文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 上文介绍了如何实现群晖Docker部署HomeAssistant,通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店,将米家,果家设备接入 Home Assistant。 基本条件…...
计算huggingface模型占用硬盘空间的实战代码
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...
Leetcode 3031. Minimum Time to Revert Word to Initial State II
Leetcode 3031. Minimum Time to Revert Word to Initial State II 1. 解题思路2. 代码实现 题目链接:3031. Minimum Time to Revert Word to Initial State II 1. 解题思路 这一题就是一个z算法的题目,算是比较套路的题目了。 关于z算法,…...
游戏后端如何实现服务器之间的负载均衡?
在当今的游戏行业中,随着游戏用户数量的不断增加,如何实现服务器之间的负载均衡成为了一个亟待解决的问题。游戏后端作为游戏的重要组成部分,承载着游戏逻辑处理和数据存储等功能,因此游戏后端的负载均衡问题尤为重要。本文将详细…...
es6中标签模板
之所以写这篇文章,是因为标签模板是一个很容易让人忽略的知识点 首先我们已经非常熟悉模板字符串的使用方法 const name "诸葛亮" const templateString hello, My name is ${name}标签模板介绍 这里的标签模板其实不是模板,而是函数调用…...
二级C语言笔试1
(总分96,考试时间90分钟) 一、选择题 下列各题A)、B)、C)、D)4个选项中,只有1个选项是正确的。 1. 有以下程序: void sum(int a[]) a[0]a[-1]a[1]; main() int a[10]1,2,3,4,5,6,7,8,9,10; sum(&a[2]); printf(…...
Spring MVC跨域设置
简介 出于安全方面考虑,浏览器发起请求时,会先检查同源策略(协议、主机、端口是否与当前页面相同),不匹配则认为是跨域请求。 CORS (Cross-Origin Resource Sharing) CORS是一种机制,允许服务器声明哪些…...
基于Python的HTTP隧道安全性分析:魔法背后的锁与钥匙
当我们谈论基于Python的HTTP隧道时,不禁让人想起那些神秘的魔法门。但是,在魔法背后,我们也需要确保安全性,就像需要确保魔法不会落入邪恶之手一样。那么,基于Python的HTTP隧道在安全性方面表现如何呢?让我…...
linux的stat/lstat函数和目录遍历函数使用
stat函数: 作用:获取文件属性 函数原型:int stat(const char *pathname, struct stat *statbuf); 返回值:成功返回0 失败返回-1 struct stat { dev_t st_dev; //文件设备编号 ino_…...
HTTP MIME 类型
MIME - Multipurpose Internet Mail Extension, 多用途因特网邮件扩展,起初是为了解决不同的电子邮件系统之间搬移报文时存在的问题。MIME 在电子邮件系统中工作得非常好,因此 HTTP 也采纳了它,用它来描述并标记多媒体内容。 MIME 类…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
