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 类…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
