目录

  • 事务
    • 数据库事务 `A C I D` 属性
    • 数据库事务隔离级别
  • `Spring` 中事务传播特性
  • `Spring` 中事务的隔离级别
  • `spring aop` 原理
    • `spring aop` 方法增强
  • `spring` 事务相关源码
    • `spring` 事务切面
    • `spring`事务拦截
    • `spring` 事务同步

事务

事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。操作隔离则是指多个同时执行的事务之间应该相互独立,互不影响

数据库事务 A C I D 属性

  • 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
  • 一致性:事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
  • 隔离性:多个事务并发执行时,一个事务的执行不应影响其他事务的执行
  • 持久性:已被提交的事务对数据库的修改应该永久保存在数据库中

数据库事务隔离级别

根据 SQL92 标准,MySQLInnoDB 引擎提供四种隔离级别

  • 读未提交(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 定义了事务操作的行为,其依赖 TransactionDefinitionTransactionStatus 接口,其实大部分的事务属性和行为我们以 MySQL 数据库为例已经有过了解,这里再对应介绍下

  • PlatformTransactionManager 接口:事务管理器
  • getTransaction 方法:事务获取操作,根据事务属性定义,获取当前事务或者创建新事物
  • commit 方法:事务提交操作,注意这里所说的提交并非直接提交事务,而是根据当前事务状态执行提交或者回滚操作
  • rollback 方法:事务回滚操作,同样,也并非一定直接回滚事务,也有可能只是标记事务为只读,等待其他调用方执行回滚
  • TransactionDefinition 接口:事务属性定义
  • getPropagationBehavior 方法:返回事务的传播属性,默认是 propagation_requierd
  • getIsolationLevel 方法:返回事务隔离级别,事务隔离级别只有在创建新事务时才有效,也就是说只对应传播属性 propagation_requierdpropagation_requierd_new
  • getTimeout 方法:返回事务超时时间,以秒为单位,同样只有在创建新事务时才有效
  • isReadOnly 方法:是否优化为只读事务,支持这项属性的事务管理器会将事务标记为只读,只读事务不允许有写操作,不支持只读属性的事务管理器需要忽略这项设置,这一点跟其他事务属性定义不同,针对其他不支持的属性设置,事务管理器应该抛出异常
  • getName 方法:返回事务名称,声明式事务中默认值为“类的完全限定名.方法名”
  • TransactionStatus:当前事务状态
  • isNewTransaction 方法:当前方法是否创建了新事务(区别于使用现有事务以及没有事务)
  • hasSavepoint 方法:在嵌套事务场景中,判断当前事务是否包含保存点
  • setRollbackOnlyisRollbackOnly 方法:只读属性设置(主要用于标记事务,等待回滚)和查询
  • flush 方法:刷新底层会话中的修改到数据库,一般用于刷新如 Hibernate/JPA的会话,是否生效由具体事务资源实现决定
  • isCompleted 方法:判断当前事务是否已完成(已提交或者已回滚)

部分 spring 包含的对 PlatformTransactionManager 的实现类如下图所示:


AbstractPlatformTransactionManager 抽象类实现了 spring 事务的标准流程,其子类 DataSourceTransactionManager 是我们使用较多的 JDBC 单数据源事务管理器,而 JtaTransactionManagerJTA(Java Transaction API) 规范的实现类,另外两个则分别是 JavaEE 容器 WebLogicWebSphereJTA 事务管理器的具体实现

spring 事务切面

之前提到,spring 采用 aop 来实现声明式事务,那么事务的 aop 切面是如何织入的呢?这一点涉及到 aop 动态代理对象的生成过程

代理对象生成的核心类是 AbstractAutoProxyCreator,实现了 beanPostProcessor接口,会在 bean 初始化完成之后,通过 postProcessAfterInitialization 方法生成代理对象**,关于 beanPostProcessorbean 生命周期中的作用,可参考一些常用的spring 的扩展接口

看一下 AbstractAutoProxyCreator 类的核心代码,主要关注三个方法:postProcessAfterInitialization、wrapIfNecessarycreateProxy,为了突出核心流程,以注释代替了部分代码的具体实现,后续的源码分析也采用相同的处理

@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,类图如下:


事务拦截器 TransactionInterceptorinvoke 方法中,通过调用父类TransactionAspectSupportinvokeWithinTransaction 方法进行事务处理,该方法支持声明式事务和编程式事务

// 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、commitTransactionAfterReturningcompleteTransactionAfterThrowing 都是针对该接口编程,并不依赖于特定事务管理器,这里是对 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 对象在当前线程记录了 resourcessynchronizations 属性。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事务机制相关推荐

  1. Spring 事务机制详解

    Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考. Spring声明式事务让我们从复杂的事务处理中得到解脱.使得我们再也无 ...

  2. 布式事务实践 解决数据一致性 Spring事务机制

    Spring事务机制 介绍Spring的事务机制.事物抽象.内部事务和外部事物,以及常用的几种事务管理的实现,包括DataSource.JPA.JMS.JTA都通过实例进行说明.还有XA以及两阶段提交 ...

  3. spring 事务机制总结

    目录 什么是事务? 事务的特性(ACID)了解么? 详谈 Spring 对事务的支持 Spring 管理事务的方式有几种? Spring 事务管理接口介绍 PlatformTransactionMan ...

  4. Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现

    前言 本专题大纲如下: 事务专题大纲 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事 ...

  5. Spring中事务的使用、抽象机制及模拟Spring事务实现

    本文大纲如下: Spring事务应用大纲 编程式事务 Spring提供了两种编程式事务管理的方法 使用 TransactionTemplate 或者 TransactionalOperator. 直接 ...

  6. 事务例子_Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现...

    Spring中事务的使用示例.属性及使用中可能出现的问题 前言 本专题大纲如下: 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用 ...

  7. Spring的事务机制

    JAVA EE传统事务机制 通常有两种事务策略:全局事务和局部事务.全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持 ...

  8. Spring事务管理 与 SpringAOP

    1,Spring事务的核心接口 Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略.  ...

  9. Spring事务管理(详解+实例)

    写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: ...

最新文章

  1. 说一说MVC的CSRF(三)
  2. C/C++在Android开发中的应用
  3. php 新闻列表,php原生开发新闻站之新闻列表(二)
  4. DL之SqueezeNet:SqueezeNet算法的架构详解
  5. 机器学习-线性回归(Linear Regression)
  6. 中国首富或将易主,5500个富豪即将诞生!
  7. 服务器 ha linux,Linux 高可用(HA)集群之Heartbeat详解
  8. oracle之单行函数2
  9. nodejs ftp文件服务器,node.js自动上传ftp的脚本分享
  10. 简单实现系统托盘 - 回复 闪 的问题
  11. python stringvar.get_Python StringVar get函数什么都不返回?
  12. 只做两个龅牙门牙_孔子画像中的门牙格外突出,画师为何不给圣人开美颜?...
  13. Shifterator库 | 词移图分辨两文本用词风格差异
  14. 陈越何欣铭老师数据结构PTA08-图8 How Long Does It Take
  15. Golang sync.Cond 简介与用法
  16. win10计算机用户名修改密码,win10怎么修改登录用户名 win10修改开机密码的详细教程...
  17. Python实现图像的全景拼接,这不比ps牛逼
  18. 请求与通配符 mime 映射相匹配。请求映射到静态文件处理程序。如果有不同的前提条件,请求将映射到另一个处理程序。
  19. 在RHEL4的GNOME环境下编译安装eva0.4.1
  20. 2022-2028年中国体外诊断行业市场发展现状及竞争格局预测报告

热门文章

  1. 子弹短信真是猛如“子弹”?还是言过其实
  2. 高考数学知识点:数列压轴小题秒杀技巧
  3. 基本类型和引用类型区别
  4. 12月份的武汉免费玩
  5. Masson快速视频制作教程(03) - 选择非线性编辑软件
  6. element-ui的slot=“append“什么意思
  7. 幽默笑话-笑话-还有菜吗等
  8. LeetCode 提莫攻击
  9. 基于Attention机制的BiLSTM语音情感识别研究与系统实现
  10. ssr cmd 窗口代理设置