流程图解Spring Framework(九) Spring 如何进行事务管理的?

2024-08-28 05:58

本文主要是介绍流程图解Spring Framework(九) Spring 如何进行事务管理的?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Jdbc事务简述

首先我们来了解一下JDBC的是如何处理事务的
代码很简单

try {// 设置为手动提交,否则执行完一条sql就提交一次事务conn.setAutoCommit(false); // 实际业务操作开始ps.executeUpdate("修改表1");ps.executeUpdate("修改表2"); // 实际业务操作结束// 提交事务conn.commit();} catch (Exception e) {  // 出现异常,进行回滚conn.rollback();  e.printStackTrace();  } 

查看以上代码可以发现,有如下个流程

  1. 设置事务手动提交
  2. 业务操作
  3. 无异常,正常提交
  4. 出现异常,事务回滚

这里写图片描述

看起来也不难的哦,
但是有一个问题是,我们每一次写事务代码,都需要写一些重复的代码,

我们分析以下代码,可以看到这里面操作的都是同一个对象,类型为Connection,

这时候我们思考一下,如果我们把这个connection对象实例包装起来,是不是就可以利用AOP在业务代码前后添加connection的代码了,

我们的思路就是把业务代码设置为一个切面,我们通过在切面前置操作中开启事务,切面后置操作中提交事务或者回滚事务,是不是就好了,那么我们看一下spring是如何操作的

spring 配置事务

java config
EnableTransactionManagement注解开启

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {//其实 就是@Transactional注解的为切面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;}}
  1. 配置一个事务拦截器
  2. 配置一个AnnotationTransactionAttributeSource,用于为事务拦截器提供切面声明支持
    需要我们再配置一个事务管理器
    @Beanpublic DataSource druidDataSource(){DruidDataSource datasource = new DruidDataSource();datasource.setUrl(dbUrl);datasource.setUsername(username);datasource.setPassword(password);datasource.setDriverClassName(driverClassName);return datasource;}@Beanpublic PlatformTransactionManager txManager(DataSource druidDataSource){return new DataSourceTransactionManager(druidDataSource);}

在代码上,我们已经可以添加了@Transactional注解来是方法在事务之中即可。

源码初探

在上一小节可以看到spring中开启事务有一个关键的bean,类型是DataSourceTransactionManager,那么重点我们就看一下这个Manager做了什么,它是如何开启事务的
首先看一下类图
这里写图片描述
PlatformTransactionManager有如下三个方法

    // 获取一个有效的事务TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;// 提交事务void commit(TransactionStatus status) throws TransactionException;// 回滚事务void rollback(TransactionStatus status) throws TransactionException;

通过我们第一节的介绍,可以知道有了这三个方法即可实现事务

首先分析以下AbstractPlatformTransactionManager.getTransaction()方法

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {// 这个方法是一个abstract方法,由具体的事务管理器实现Object transaction = doGetTransaction();//判断逻辑已省略//......// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 这个方法是一个abstract方法,由具体的事务管理器实现,doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}catch (RuntimeException ex) {resume(null, suspendedResources);throw ex;}catch (Error err) {resume(null, suspendedResources);throw err;}}else {// Create "empty" transaction: no actual transaction, but potentially synchronization.if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + definition);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}

我们具体就看两个Abstrat方法的实现
DataSourceTransactionManager.doGetTransaction

protected Object doGetTransaction() {DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());// 从数据源获取一个连接ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);txObject.setConnectionHolder(conHolder, false);return txObject;}

DataSourceTransactionManager.doBegin

protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {if (txObject.getConnectionHolder() == null ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = this.dataSource.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);// 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).if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}// 设置手动提交con.setAutoCommit(false);}txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// Bind the session holder to the thread.if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, this.dataSource);txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}}

DataSourceTransactionManager.doCommit

    @Overrideprotected 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 new TransactionSystemException("Could not commit JDBC transaction", ex);}}

DataSourceTransactionManager.doCommit

@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 new TransactionSystemException("Could not roll back JDBC transaction", ex);}}

可以看到spring的处理和我们手写的处理没什么两样,但是仅凭上面的,我们也没有看到我们的业务代码是在哪里实现的,我们来看一下@Transactional的实现
TransactionInterceptor
这里写图片描述
首先我们可以看到MethodInteceptor,我们就可以知道这是一个方法拦截器,主要看他的invoke方法

    public Object invoke(final MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}});}

我们可以看到有一个callback,不妨猜一下就是在事务开启成功后调用

查看TransactionAspectSupport.invokeWithinTransaction方法

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {// 获取事务属性,Transactional注解final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);// 获取事务拦截器final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.// 创建事务TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 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) {// target invocation exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;}else {// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.// 编程式事务try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus status) {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {// A RuntimeException: will lead to a rollback.if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {// A normal return value: will lead to a commit.return new ThrowableHolder(ex);}}finally {cleanupTransactionInfo(txInfo);}}});// Check result: It might indicate a Throwable to rethrow.if (result instanceof ThrowableHolder) {throw ((ThrowableHolder) result).getThrowable();}else {return result;}}catch (ThrowableHolderException ex) {throw ex.getCause();}}}

以上可以看到,利用这个拦截器,可以实现方法的切面调用,进行事务的开启。这样我们就可以知道了spring是如何管理事务的了

有关事务传播行为和隔离级别的博客可以详见
https://blog.csdn.net/u013076044/article/details/79996198
https://blog.csdn.net/u013076044/article/details/79950916

这篇关于流程图解Spring Framework(九) Spring 如何进行事务管理的?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1113922

相关文章

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

MySQL深分页进行性能优化的常见方法

《MySQL深分页进行性能优化的常见方法》在Web应用中,分页查询是数据库操作中的常见需求,然而,在面对大型数据集时,深分页(deeppagination)却成为了性能优化的一个挑战,在本文中,我们将... 目录引言:深分页,真的只是“翻页慢”那么简单吗?一、背景介绍二、深分页的性能问题三、业务场景分析四、

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

Java中Integer128陷阱

《Java中Integer128陷阱》本文主要介绍了Java中Integer与int的区别及装箱拆箱机制,重点指出-128至127范围内的Integer值会复用缓存对象,导致==比较结果为true,下... 目录一、Integer和int的联系1.1 Integer和int的区别1.2 Integer和in

SpringSecurity整合redission序列化问题小结(最新整理)

《SpringSecurity整合redission序列化问题小结(最新整理)》文章详解SpringSecurity整合Redisson时的序列化问题,指出需排除官方Jackson依赖,通过自定义反序... 目录1. 前言2. Redission配置2.1 RedissonProperties2.2 Red

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

JSONArray在Java中的应用操作实例

《JSONArray在Java中的应用操作实例》JSONArray是org.json库用于处理JSON数组的类,可将Java对象(Map/List)转换为JSON格式,提供增删改查等操作,适用于前后端... 目录1. jsONArray定义与功能1.1 JSONArray概念阐释1.1.1 什么是JSONA

Java JDK1.8 安装和环境配置教程详解

《JavaJDK1.8安装和环境配置教程详解》文章简要介绍了JDK1.8的安装流程,包括官网下载对应系统版本、安装时选择非系统盘路径、配置JAVA_HOME、CLASSPATH和Path环境变量,... 目录1.下载JDK2.安装JDK3.配置环境变量4.检验JDK官网下载地址:Java Downloads