当前位置: 首页 > news >正文

Spring的xml方式声明式事务控制

Spring的xml方式声明式事务控制

一、概述

前面我们讲了Spring的AOP配置,但是这种直接配置在开发中使用得较少,一般是事务控制使用得较多,在事务控制中利用的也是AOP的原理,只不过把事务是当作AOP当中的通知,且这个通知由Spring来产生,我们只负责配置,下面就事务的一些知识和xml方式声明式事务控制进行讲解:

二、实验

以转账来进行实验,如果让加钱和减钱各自一个事务的话,那么当出现异常情况时,可能是减钱了,但是另一个账户没有加钱的情况:所以要将加钱和减钱组成一个事务:

<?xml version="1.0"encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "><!--组件扫描--><context:component-scan base-package="com.itheima"/><!--加载properties文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置数据源信息--><bean id="dataSource"class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName"value="${jdbc.driver}"></property><property name="url"value="${jdbc.url}"></property><property name="username"value="${jdbc.username}"></property><property name="password"value="${jdbc.password}"></property></bean><!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器--><beanclass="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource"ref="dataSource"></property></bean><!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器--><beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage"value="com.itheima.mapper"></property></bean><!--还要配置事物管理器,在编程式事务中讲过事物管理器具有回滚、获得、提交事务对象的功能,不同框架的事务管理器不同--><!--底层是mybatis,mybatis底层是jdbc,jdbc需要connection,所以要数据源--><beanclass="org.springframework.jdbc.datasource.DataSourceTransactionManager"id="transactionManager"><property name="dataSource"ref="dataSource"/></bean><!--然后是要配置通知,因为这个通知是Spring提供的,所以需要用到tx标签--><tx:advice id="txadvice"transaction-manager="transactionManager"><tx:attributes><tx:method name="*"/></tx:attributes></tx:advice><!--使用advisor配置AOP--><aop:config><aop:pointcut id="txPointcut"expression="execution(* com.itheima.service.impl.*.*(..))"/><!--配置织入--><aop:advisor advice-ref="txadvice"pointcut-ref="txPointcut"/></aop:config></beans>

先是从下往上配要用Spring的事务的话(也就是通知)就需要使用advisor配置织入,而且配置通知时需要使用tx标签,其中事务中又要配transaction-manager事务管理器,一般要配置事务管理器的话都要实现,它的继承关系如下:

在这里Dao层使用的框架是mybatis,mybatis底层是jdbc,jdbc需要connection,所以要数据源,配置的是DataSourceTransactionManager,且要指定数据源。同时在通知中还要指定method。这样加钱和减钱组成一个事务,出现异常加钱和减钱都不能完成了:

packagecom.itheima.service.impl;importcom.itheima.mapper.AccountMapper;importcom.itheima.service.AccountService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Isolation;importorg.springframework.transaction.annotation.Propagation;importorg.springframework.transaction.annotation.Transactional;@Service("accountService")@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)publicclassAccountServiceImpl2implementsAccountService{@AutowiredprivateAccountMapperaccountMapper;@OverridepublicvoidtransferMoney(StringoutAccount,StringinAccount,Integermoney){// 如果是编程类事务的话:先开一个事务,然后try catch以下有异常就回滚accountMapper.decrMoney(outAccount,money);inti=1/0;accountMapper.incrMoney(inAccount,money);}publicvoidregistAccount(){}}
packagecom.itheima;importcom.itheima.config.SpringConfig;importcom.itheima.service.AccountService;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassAccountTest{publicstaticvoidmain(String[]args){ApplicationContextapp=newClassPathXmlApplicationContext("applicationContext.xml");//ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);AccountServiceaccountService=app.getBean(AccountService.class);accountService.transferMoney("tom","lucy",500);}}

结果:

钱是没有转成功的。

在这里值得注意的是tx的attributes属性:

  • name:方法名称 *代表通配符 添加操作 addUser、addAccount、addOrders 可统一配置为 add *

  • isolation:事务的隔离级别,用于解决事务并发问题

  • timeout:超时时间,默认值为-1,单位是s

  • read-only:是否为只读模式,查询操作建议设置为只读

  • propagation:事务的传播行为,用于解决业务方法调用业务方法时出现的事务嵌套问题

    要是没有设置这些属性。会应用数据库默认的,但是必须加上<tx:method name=“*”/>,在项目中常常是这样设置(各大功能一个,留一个保底的):

<tx:method name="add*"/><tx:method name="update*"/><tx:method name="delete*"/><tx:method name="select*"/><tx:method name="*"/>

一般是REQUIRED和SUPPORTS使用得多需要记住。

三、原理解析

​ tx:advice是通过xml配置的当然是找它的handler了:

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

进入TxNamespaceHandler:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//packageorg.springframework.transaction.config;importorg.springframework.beans.factory.xml.NamespaceHandlerSupport;importorg.w3c.dom.Element;publicclassTxNamespaceHandlerextendsNamespaceHandlerSupport{staticfinalStringTRANSACTION_MANAGER_ATTRIBUTE="transaction-manager";staticfinalStringDEFAULT_TRANSACTION_MANAGER_BEAN_NAME="transactionManager";publicTxNamespaceHandler(){}staticStringgetTransactionManagerName(Elementelement){returnelement.hasAttribute("transaction-manager")?element.getAttribute("transaction-manager"):"transactionManager";}publicvoidinit(){this.registerBeanDefinitionParser("advice",newTxAdviceBeanDefinitionParser());this.registerBeanDefinitionParser("annotation-driven",newAnnotationDrivenBeanDefinitionParser());this.registerBeanDefinitionParser("jta-transaction-manager",newJtaTransactionManagerBeanDefinitionParser());}}

进入TxAdviceBeanDefinitionParser(),既然是Parser肯定有Parser方法,不在本类就在父类和爷爷类,这个是在爷爷类:

publicabstractclassAbstractBeanDefinitionParserimplementsBeanDefinitionParser{publicstaticfinalStringID_ATTRIBUTE="id";publicstaticfinalStringNAME_ATTRIBUTE="name";publicAbstractBeanDefinitionParser(){}@NullablepublicfinalBeanDefinitionparse(Elementelement,ParserContextparserContext){AbstractBeanDefinitiondefinition=this.parseInternal(element,parserContext);if(definition!=null&&!parserContext.isNested()){try{Stringid=this.resolveId(element,definition,parserContext);if(!StringUtils.hasText(id)){parserContext.getReaderContext().error("Id is required for element '"+parserContext.getDelegate().getLocalName(element)+"' when used as a top-level tag",element);}String[]aliases=null;if(this.shouldParseNameAsAliases()){Stringname=element.getAttribute("name");if(StringUtils.hasLength(name)){aliases=StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));}}BeanDefinitionHolderholder=newBeanDefinitionHolder(definition,id,aliases);this.registerBeanDefinition(holder,parserContext.getRegistry());if(this.shouldFireEvents()){BeanComponentDefinitioncomponentDefinition=newBeanComponentDefinition(holder);this.postProcessComponentDefinition(componentDefinition);parserContext.registerComponent(componentDefinition);}}catch(BeanDefinitionStoreExceptionex){Stringmsg=ex.getMessage();parserContext.getReaderContext().error(msg!=null?msg:ex.toString(),element);returnnull;}}returndefinition;}

让端点到达tx:advice:

然后往下看还有一段:

BeanDefinitionHolderholder=newBeanDefinitionHolder(definition,id,aliases);this.registerBeanDefinition(holder,parserContext.getRegistry());

这是注册BeanDefinition,看对应的holder:

可以看到这是名为txadvice,值为TransactionInterceptor的类。

那么我们就分析TransactionInterceptor:

publicclassTransactionInterceptorextendsTransactionAspectSupportimplementsMethodInterceptor,Serializable{publicTransactionInterceptor(){}

发现它实现了MethodInterceptor接口,依据之前学过的Advisor配置AOP织入可以知道这相当于配置环绕通知,是通过它的invoke方法来配置的:

@NullablepublicObjectinvoke(MethodInvocationinvocation)throwsThrowable{Class<?>targetClass=invocation.getThis()!=null?AopUtils.getTargetClass(invocation.getThis()):null;Methodvar10001=invocation.getMethod();invocation.getClass();returnthis.invokeWithinTransaction(var10001,targetClass,invocation::proceed);}

targetClass:

进入invokeWithinTransaction:

}}else{TransactionInfotxInfo=this.createTransactionIfNecessary(ptm,txAttr,joinpointIdentification);ObjectretVal;try{retVal=invocation.proceedWithInvocation();}catch(Throwableex){

该有的都返回了,createTransactionIfNecessary就是开事务了,看到createTransactionIfNecessary,进入:

status=tm.getTransaction(txAttr);

去看tm是谁:

protectedTransactionInfocreateTransactionIfNecessary(@NullablePlatformTransactionManagertm,@NullableTransactionAttributetxAttr,finalStringjoinpointIdentification){

tm是PlatformTransactionManager,平台事务管理器

同时我们也看到了createTransactionIfNecessary中的参数ptm,那么它是什么呢?

PlatformTransactionManagerptm=this.asPlatformTransactionManager(tm);

tm又是:

TransactionManagertm=this.determineTransactionManager(txAttr);

到此为止,后面是最终是通过解析xml文件获取的。

再来看status = tm.getTransaction(txAttr);,进入getTransaction():

publicfinalTransactionStatusgetTransaction(@NullableTransactionDefinitiondefinition)throwsTransactionException{

TransactionStatus事务管理器状态,编程式事务的一种,(PlatformTransactionManager也是其中一种)

同时又看到这一句:

returnthis.startTransaction(def,transaction,debugEnabled,suspendedResources);

这是开启事务,就执行完了createTransactionIfNecessary,所以是在执行createTransactionIfNecessary时内部已经开事务了,

往createTransactionIfNecessary下又可以看到:

this.commitTransactionAfterReturning(txInfo);

就是提交事务了。

而开启事务和提交事务又是在invoke方法执行的,所以而这个invoke方法又是属于TransactionInterceptor(实现了 MethodInterceptor),所以我们在配置的时候就通过Advisor来配置TransactionInterceptor,实际上声明式事务本质上就是AOP。

http://www.jsqmd.com/news/453527/

相关文章:

  • 2026年江苏宇灿智能装备有限公司产品好用吗,宇灿智能装备可信度高吗排名 - myqiye
  • RetinaFace在Linux系统上的部署教程:从零开始搭建人脸检测环境
  • Gemma-3-12B-IT在STM32嵌入式开发中的边缘计算应用
  • Python字符串strip函数作用
  • MouseEngine 进一步美化你的光标
  • 【2025最新】基于SpringBoot+Vue的产业园区智慧公寓管理系统管理系统源码+MyBatis+MySQL
  • 【书生·浦语】internlm2-chat-1.8b效果惊艳:长篇小说续写风格一致性保持演示
  • GLM-Image WebUI部署教程:系统监控(GPU温度/显存/负载)集成方案
  • 键位映射操作:KeybMap的使用方法
  • Java Web 车险理赔信息管理系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • RVC在内容创作中的应用:短视频配音/虚拟主播落地实践
  • Hash哈希表以及代码
  • 雷达原理(第三版) 丁鹭飞 中最主要的公式
  • Flutter SVG图片Demo
  • 编译器优化屏障使用
  • 基于SpringBoot+Vue的船舶监造系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】
  • 【ArcGIS技巧】表格批量转图片(emf格式)方便相对路径索引表格
  • Qwen3-ASR-0.6B语音识别实测:轻量级模型,专业级效果,小白也能用
  • redis具体情况介绍
  • 云容笔谈微信小程序前端开发实战:打造个人AI画师工具
  • HeyGem数字人视频生成系统批量版:5分钟快速部署,新手也能轻松上手
  • L1-020 帅到没朋友(分数20)
  • 索引和事务
  • 一键部署梦幻动漫魔法工坊:快速搭建你的二次元创作平台
  • 探寻2026年贵阳诚信的网络营销培训学校,怎么选择更合适 - myqiye
  • 聊聊江苏宇灿智能装备技术水平怎么样,其管道加热器值得推荐吗 - 工业推荐榜
  • 春联生成模型-中文-base内存优化:解决大并发下的显存溢出问题
  • Qwen2-VL-2B-Instruct保姆级教程:Pillow+Sentence-Transformers环境配置全步骤
  • AWPortrait-Z快速入门:3步搞定你的第一张AI肖像照
  • RVC语音变声器教育应用:语言学习发音纠正与语音模仿训练