Spring事务机制
目录
- 事务
- 数据库事务 `A C I D` 属性
- 数据库事务隔离级别
- `Spring` 中事务传播特性
- `Spring` 中事务的隔离级别
- `spring aop` 原理
- `spring aop` 方法增强
- `spring` 事务相关源码
- `spring` 事务切面
- `spring`事务拦截
- `spring` 事务同步
事务
事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。操作隔离则是指多个同时执行的事务之间应该相互独立,互不影响
数据库事务 A C I D
属性
- 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
- 一致性:事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
- 隔离性:多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性:已被提交的事务对数据库的修改应该永久保存在数据库中
数据库事务隔离级别
根据 SQL92
标准,MySQL
的 InnoDB
引擎提供四种隔离级别
- 读未提交(
Read Uncommited
) - 读已提交(
Read Commited
):oracle
默认的隔离级别 - 可重复读(
Repeatable Read
):mysql
默认的隔离级别,其可避免脏读和不可重复读,但不能避免幻读 - 串行化(
Serializable
)
不同的隔离级别带来不同的问题
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 会 | 会 | 会 |
读已提交 | 不会 | 会 | 会 |
可重复读 | 不会 | 不会 | 会 |
可串行化 | 不会 | 不会 | 不会 |
Spring
中事务传播特性
propagation_requierd
: 如果当前没有事务,就新建一个事务。如果存在事务,就加入到这个事务中。这是spring
默认的propagation_supports
: 支持当前事务。如果当前没有事务,就以非事务方法执行propagation_mandatory
: 使用当前事务,如果没有当前事务,就抛出异常propagation_requierd_new
: 新建事务,如果当前存在事务,把当前事务挂起propagation_not_supported
: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起propagation_never
: 以非事务方式执行操作,如果当前事务存在则抛出异常propagation_nested
: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与
propagation_required
类似的操作
Spring
中事务的隔离级别
隔离级别:若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读
枚举 Isolation
中定义了 5
个表示隔离级别的值
Isolation.DEFAULT
:使用各个数据库默认的隔离级别,是Spring
默认的隔离级别Isolation.READ_UNCOMMITTED
:读取未提交数据(会出现脏读,不可重复读)Isolation.READ_COMMITTED
:读取已提交数据(会出现不可重复读和幻读)Isolation.REPEATABLE_READ
:可重复读(会出现幻读)Isolation.SERIALIZABLE
:串行化
spring aop
原理
动态代理是 spring
实现 aop
的默认方式,分为两种:
JDK
动态代理和CGLIB
动态代理。JDK
动态代理面向接口,通过反射生成目标代理接口的匿名实现类CGLIB
动态代理则通过继承,使用字节码增强技术(或者objenesis
类库)为目标代理类生成代理子类
spring
默认对接口实现使用 JDK
动态代理,对具体类使用 CGLIB
,同时也支持配置全局使用 CGLIB
来生成代理对象
spring aop
方法增强
- 前置增强(
org.springframework.aop.BeforeAdvice
):在目标方法执行之前进行增强 - 后置增强(
org.springframework.aop.AfterReturningAdvice
):在目标方法执行之后进行增强 - 环绕增强(
org.aopalliance.intercept.MethodInterceptor
):在目标方法执行前后都执行增强 - 异常抛出增强(
org.springframework.aop.ThrowsAdvice
):在目标方法抛出异常后执行增强 - 引介增强(
org.springframework.aop.IntroductionInterceptor
):为目标类添加新的方法和属性
声明式事务的实现就是通过 环绕增强
的方式,在 目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务
,事务拦截器的继承关系图可以体现这一点
spring
事务相关源码
统一一致的事务抽象是 spring
框架的一大优势,无论是全局事务还是本地事务,JTA、JDBC、Hibernate
还是 JPA,spring
都使用统一的编程模型,使得应用程序可以很容易地在全局事务与本地事务,或者不同的事务框架之间进行切换。下图是 spring
事务抽象的核心类图:
接口 PlatformTransactionManager
定义了事务操作的行为,其依赖 TransactionDefinition
和 TransactionStatus
接口,其实大部分的事务属性和行为我们以 MySQL
数据库为例已经有过了解,这里再对应介绍下
PlatformTransactionManager
接口:事务管理器getTransaction
方法:事务获取操作,根据事务属性定义,获取当前事务或者创建新事物commit
方法:事务提交操作,注意这里所说的提交并非直接提交事务,而是根据当前事务状态执行提交或者回滚操作rollback
方法:事务回滚操作,同样,也并非一定直接回滚事务,也有可能只是标记事务为只读,等待其他调用方执行回滚TransactionDefinition
接口:事务属性定义getPropagationBehavior
方法:返回事务的传播属性,默认是propagation_requierd
getIsolationLevel
方法:返回事务隔离级别,事务隔离级别只有在创建新事务时才有效,也就是说只对应传播属性propagation_requierd
和propagation_requierd_new
getTimeout
方法:返回事务超时时间,以秒为单位,同样只有在创建新事务时才有效isReadOnly
方法:是否优化为只读事务,支持这项属性的事务管理器会将事务标记为只读,只读事务不允许有写操作,不支持只读属性的事务管理器需要忽略这项设置,这一点跟其他事务属性定义不同,针对其他不支持的属性设置,事务管理器应该抛出异常getName
方法:返回事务名称,声明式事务中默认值为“类的完全限定名.方法名”TransactionStatus
:当前事务状态isNewTransaction
方法:当前方法是否创建了新事务(区别于使用现有事务以及没有事务)hasSavepoint
方法:在嵌套事务场景中,判断当前事务是否包含保存点setRollbackOnly
和isRollbackOnly
方法:只读属性设置(主要用于标记事务,等待回滚)和查询flush
方法:刷新底层会话中的修改到数据库,一般用于刷新如Hibernate/JPA
的会话,是否生效由具体事务资源实现决定isCompleted
方法:判断当前事务是否已完成(已提交或者已回滚)
部分 spring
包含的对 PlatformTransactionManager
的实现类如下图所示:
AbstractPlatformTransactionManager
抽象类实现了 spring
事务的标准流程,其子类 DataSourceTransactionManager
是我们使用较多的 JDBC
单数据源事务管理器,而 JtaTransactionManager
是 JTA(Java Transaction API)
规范的实现类,另外两个则分别是 JavaEE
容器 WebLogic
和 WebSphere
的 JTA
事务管理器的具体实现
spring
事务切面
之前提到,spring
采用 aop
来实现声明式事务,那么事务的 aop
切面是如何织入的呢?这一点涉及到 aop
动态代理对象的生成过程
代理对象生成的核心类是 AbstractAutoProxyCreator
,实现了 beanPostProcessor
接口,会在 bean
初始化完成之后,通过 postProcessAfterInitialization
方法生成代理对象**,关于 beanPostProcessor
在 bean
生命周期中的作用,可参考一些常用的spring 的扩展接口
看一下 AbstractAutoProxyCreator
类的核心代码,主要关注三个方法:postProcessAfterInitialization、wrapIfNecessary
和 createProxy
,为了突出核心流程,以注释代替了部分代码的具体实现,后续的源码分析也采用相同的处理
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {// 创建代理对象return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 参数检查,跳过已经执行过代理对象生成,或者已知的不需要生成代理对象的Bean...// Create proxy if we have advice.// 查询当前Bean所有的AOP增强配置,最终是通过AOPUtils工具类实现Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 执行AOP织入,创建代理对象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;
}protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}// 实例化代理工厂类ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);// 当全局使用动态代理时,设置是否需要对目标Bean强制使用CGLIB动态代理...// 构建AOP增强顾问,包含框架公共增强和应用程序自定义增强// 设置proxyFactory属性,如增强、目标类、是否允许变更等...// 创建代理对象return proxyFactory.getProxy(getProxyClassLoader());
}
最后是通过调用 ProxyFactory.getProxy(java.lang.ClassLoader)
方法来创建代理对象的
// ProxyFactory.class
public Object getProxy(ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);
}// ProxyFactory 父类 ProxyCreatorSupport.class
protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);
}public ProxyCreatorSupport() {this.aopProxyFactory = new DefaultAopProxyFactory();
}
ProxyFactory
的父类构造器实例化了 DefaultAopProxyFactory
类,从其源代码我们可以看到 spring
动态代理方式选择策略的实现:如果目标类 optimize,proxyTargetClass
属性设置为 true
或者未指定需要代理的接口,则使用 CGLIB
生成代理对象,否则使用 JDK
动态代理
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 如果optimize,proxyTargetClass属性设置为true或者未指定代理接口,则使用CGLIB生成代理对象if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();// 参数检查,targetClass为空抛出异常...// 目标类本身是接口或者代理对象,仍然使用JDK动态代理if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// Objenesis是一个可以不通过构造器创建子类的java工具类库// 作为Spring 4.0后CGLIB的默认实现return new ObjenesisCglibAopProxy(config);}else {// 否则使用JDK动态代理return new JdkDynamicAopProxy(config);}}...
}
spring
事务拦截
我们已经了解了 aop
切面织入生成代理对象的过程,当 bean
方法通过代理对象调用时,会触发对应的 aop
增强拦截器,前面提到声明式事务是一种环绕增强,对应接口为 MethodInterceptor
,事务增强对该接口的实现为 TransactionInterceptor
,类图如下:
事务拦截器 TransactionInterceptor
在 invoke
方法中,通过调用父类TransactionAspectSupport
的 invokeWithinTransaction
方法进行事务处理,该方法支持声明式事务和编程式事务
// TransactionInterceptor.class
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {// 获取targetClass...// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {// 实际执行目标方法return invocation.proceed();}});
}// TransactionInterceptor 父类 TransactionAspectSupport.class
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 {// 带回调的事务执行处理,一般用于编程式事务...}
}
在讲 spring
事务抽象时,有提到事务抽象的核心接口为PlatformTransactionManager
,它负责管理事务行为,包括事务的获取、提交和回滚。在 invokeWithinTransaction
方法中,我们可以看到createTransactionIfNecessary、commitTransactionAfterReturning
和completeTransactionAfterThrowing
都是针对该接口编程,并不依赖于特定事务管理器,这里是对 spring
事务抽象的实现
//TransactionAspectSupport.class
protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {...TransactionStatus status = null;if (txAttr != null) {if (tm != null) {// 获取事务status = tm.getTransaction(txAttr);...
}protected void commitTransactionAfterReturning(TransactionInfo txInfo) {if (txInfo != null && txInfo.hasTransaction()) {...// 提交事务txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}
}protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.hasTransaction()) {...if (txInfo.transactionAttribute.rollbackOn(ex)) {try {// 异常类型为回滚异常,执行事务回滚txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}...} else {try {// 异常类型为非回滚异常,仍然执行事务提交txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}...
}protected final class TransactionInfo {private final PlatformTransactionManager transactionManager;...
另外,在获取事务时,AbstractPlatformTransactionManager#doBegin
方法负责开启新事务,在 DataSourceTransactionManager
有如下代码:
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {// 获取数据库连接con...if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}...
}
这里才真正开启了数据库事务
spring
事务同步
提到事务传播机制时,我们经常提到一个条件“如果当前已有事务”,那么 spring
是如何知道当前是否已经开启了事务呢?在 AbstractPlatformTransactionManager
中是这样做的:
// AbstractPlatformTransactionManager.class
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {Object transaction = doGetTransaction();// 参数为 null时构造默认值...if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(definition, transaction, debugEnabled);}...// 获取当前事务对象
protected abstract Object doGetTransaction() throws TransactionException;// 判断当前事务对象是否包含活跃事务
protected boolean isExistingTransaction(Object transaction) throws TransactionException {return false;
}
注意 getTransaction
方法是 final
的,无法被子类覆盖,保证了获取事务流程的一致和稳定。抽象方法 doGetTransaction
获取当前事务对象,方法 isExistingTransaction
判断当前事务对象是否存在活跃事务,具体逻辑由特定事务管理器实现,看下我们使用最多的 DataSourceTransactionManager
对应的实现:
// DataSourceTransactionManager.class
@Override
protected Object doGetTransaction() {DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);txObject.setConnectionHolder(conHolder, false);return txObject;
}@Override
protected boolean isExistingTransaction(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
可以看到,获取当前事务对象时,使用了TransactionSynchronizationManager#getResource
方法,类图如下:
TransactionSynchronizationManager
通过 ThreadLocal
对象在当前线程记录了 resources
和 synchronizations
属性。resources
是一个 HashMap
,用于记录当前参与事务的事务资源,方便进行事务同步,在 DataSourceTransactionManager
的例子中就是以 dataSource
作为 key
,保存了数据库连接,这样在同一个线程中,不同的方法调用就可以通过 dataSource
获取相同的数据库连接,从而保证所有操作在一个事务中进行。synchronizations
属性是一个 TransactionSynchronization
对象的集合,AbstractPlatformTransactionManager
类中定义了事务操作各个阶段的调用流程,以事务提交为例:
// AbstractPlatformTransactionManager.class
private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {prepareForCommit(status);triggerBeforeCommit(status);triggerBeforeCompletion(status);....else if (status.isNewTransaction()) {// 记录日志...doCommit(status);}...// 事务调用异常处理...try {triggerAfterCommit(status);}finally {triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}}
}
我们可以看到,有很多 trigger
前缀的方法,这些方法用于在事务操作的各个阶段触发回调,从而可以精确控制在事务执行的不同阶段所要执行的操作,这些回调实际上都通过 TransactionSynchronizationUtils
来实现,它会遍历TransactionSynchronizationManager#synchronizations
集合中的TransactionSynchronization
对象,然后分别触发集合中各个元素对应方法的调用。例如:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// do something after commit}
});
这段代码就在当前线程的事务 synchronizations
属性中,添加了一个自定义同步类,如果当前存在事务,那么在事务管理器执行事务提交之后,就会触发 afterCommit
方法,可以通过这种方式在事务执行的不同阶段自定义一些操作
Spring事务机制相关推荐
- Spring 事务机制详解
Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考. Spring声明式事务让我们从复杂的事务处理中得到解脱.使得我们再也无 ...
- 布式事务实践 解决数据一致性 Spring事务机制
Spring事务机制 介绍Spring的事务机制.事物抽象.内部事务和外部事物,以及常用的几种事务管理的实现,包括DataSource.JPA.JMS.JTA都通过实例进行说明.还有XA以及两阶段提交 ...
- spring 事务机制总结
目录 什么是事务? 事务的特性(ACID)了解么? 详谈 Spring 对事务的支持 Spring 管理事务的方式有几种? Spring 事务管理接口介绍 PlatformTransactionMan ...
- Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现
前言 本专题大纲如下: 事务专题大纲 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事 ...
- Spring中事务的使用、抽象机制及模拟Spring事务实现
本文大纲如下: Spring事务应用大纲 编程式事务 Spring提供了两种编程式事务管理的方法 使用 TransactionTemplate 或者 TransactionalOperator. 直接 ...
- 事务例子_Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现...
Spring中事务的使用示例.属性及使用中可能出现的问题 前言 本专题大纲如下: 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用 ...
- Spring的事务机制
JAVA EE传统事务机制 通常有两种事务策略:全局事务和局部事务.全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持 ...
- Spring事务管理 与 SpringAOP
1,Spring事务的核心接口 Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略. ...
- Spring事务管理(详解+实例)
写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: ...
最新文章
- 说一说MVC的CSRF(三)
- C/C++在Android开发中的应用
- php 新闻列表,php原生开发新闻站之新闻列表(二)
- DL之SqueezeNet:SqueezeNet算法的架构详解
- 机器学习-线性回归(Linear Regression)
- 中国首富或将易主,5500个富豪即将诞生!
- 服务器 ha linux,Linux 高可用(HA)集群之Heartbeat详解
- oracle之单行函数2
- nodejs ftp文件服务器,node.js自动上传ftp的脚本分享
- 简单实现系统托盘 - 回复 闪 的问题
- python stringvar.get_Python StringVar get函数什么都不返回?
- 只做两个龅牙门牙_孔子画像中的门牙格外突出,画师为何不给圣人开美颜?...
- Shifterator库 | 词移图分辨两文本用词风格差异
- 陈越何欣铭老师数据结构PTA08-图8 How Long Does It Take
- Golang sync.Cond 简介与用法
- win10计算机用户名修改密码,win10怎么修改登录用户名 win10修改开机密码的详细教程...
- Python实现图像的全景拼接,这不比ps牛逼
- 请求与通配符 mime 映射相匹配。请求映射到静态文件处理程序。如果有不同的前提条件,请求将映射到另一个处理程序。
- 在RHEL4的GNOME环境下编译安装eva0.4.1
- 2022-2028年中国体外诊断行业市场发展现状及竞争格局预测报告