新闻资讯  快讯  焦点  财经  政策  社会
互 联 网   电商  金融  数据  计算  技巧
生活百科  科技  职场  健康  法律  汽车
手机百科  知识  软件  修理  测评  微信
软件技术  应用  系统  图像  视频  经验
硬件技术  知识  技术  测评  选购  维修
网络技术  硬件  软件  设置  安全  技术
程序开发  语言  移动  数据  开源  百科
安全防护  资讯  黑客  木马  病毒  移动
站长技术  搜索  SEO  推广  媒体  移动
财经百科  股票  知识  理财  财务  金融
教育考试  育儿  小学  高考  考研  留学
您当前的位置:首页 > IT百科 > 程序开发 > 语言 > JAVA

详解阿里P7架构师是怎么在Spring中实现事务暂停

时间:2019-08-13 10:12:04  来源:  作者:
详解阿里P7架构师是怎么在Spring中实现事务暂停

 

摘要

Spring框架是一个流行的基于轻量级控制反转容器的JAVA/J2EE应用框架,尤其在数据访问和事务管理方面的能力是众所周知的。Spring的声明性事务分离可以应用到任何POJO目标对象,并且包含所有EJB基于容器管理事务中的已声明事务。后台的事务管理器支持简单的基于JDBC的事务和全功能的基于JTA的J2EE事务。

这篇文章详细的讨论了Spring的事务管理特性。重点是如何在使用JTA作为后台事务策略的基础上让POJO利用Spring的声明性事务,这也显示了Spring的事务服务可以无缝地与J2EE服务器(如BEA WebLogic Server的事务协调器)的事务协调器进行交互,作为EJB CMT传统事务分离方式的一个替代者。

详解阿里P7架构师是怎么在Spring中实现事务暂停

 

POJO的声明性事务

作为Spring声明性事务分离方式的样例,让我们来看一下Spring的样例应用PetClinic的中心服务外观中的配置:

清单1

<bean id="dataSource" 
 class="org.springframework.jndi.JndiObjectFactoryBean">
 <property name="jndiName">
 <value>java:comp/env/jdbc/petclinic</value>
 </property>
</bean>
<bean id="transactionManager" 
 class="org.springframework.transaction.jta.JtaTransactionManager"/>
<bean id="clinicTarget" 
 class="org.springframework.samples.petclinic.jdbc.JdbcClinic">
 <property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<bean id="clinic" 
 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
 <property name="transactionManager"><ref bean="transactionManager"/></property>
 <property name="target"><ref bean="clinicTarget"/></property>
 <property name="transactionAttributes">
 <props>
 <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
 <prop key="store*">PROPAGATION_REQUIRED</prop>
 </props>
 </property>
</bean>

他遵循Spring的标准XMLBean定义格式。定义了

  1. 一个DataSource引用,指向一个JNDI位置—在J2EE服务器管理下这将从JNDI环境中获取特定的DataSource。
  2. 一个应用服务实现—这是一个POJO,封装了业务和数据访问逻辑。在这里实现了应用中的Clinic服务接口。
  3. 一个应用服务的事务代理—这个代理为目标服务定义了事务属性,匹配特定的方法名模式并为之创建相应的事务。在实际的事务管理中,代理指向一个PlatformTransactionManager实现。

注意:除了显式的代理定义,Spring还支持自动代理机制和通过Commons Attributes或J2SE 5.0注解实现源程序级的元数据使用。这些可选方法的讨论超过了本文的范围。可以参考Spring的文档来了解相关细节。

业务接口和业务实现是特定于应用的并且不需要关心Spring或者Spring的事务管理。普通Java对象可以作为服务的目标对象,而且任何普通Java接口可以作为服务的接口。下面是一个Clinic接口的示例:

清单2

public interface Clinic {
 Pet loadPet(int id);
 void storePet(Pet pet);
 ...
}

这个接口的实现如下显示,假设他使用JDBC来执行必要的数据访问。他通过bean属性的设置方法来获取JDBC的DataSource;这与上面的配置中的dataSource属性定义相对应。

清单3

public class JdbcClinic implements Clinic {
 private DataSource dataSource;
 public void setDataSource(DataSource dataSource) {
 this.dataSource = dataSource;
 }
 public Pet loadPet(int id) {
 try {
 Connection con = this.dataSource.getConnection();
 ...
 }
 catch (SQLException ex) {
 ...
 }
 }
 public void storePet(Pet pet) {
 try {
 Connection con = this.dataSource.getConnection();
 ...
 }
 catch (SQLException ex) {
 ...
 }
 }
 ...
}

如你所见,代码相当直接。我们使用一个简单的Java对象,而事务管理由事务代理来处理,这个我们会在下面讨论。

注意在PetClinic示例应用中实际的基于JDBC的Clinic实现利用了Spring的JDBC支持类来避免直接使用JDBC的API。虽然Spring的事务管理也可以与普通的基于JDBC实现一起工作,就向上面的示例。

定义事务代理

除了JdbcClinic实例以外,配置中也定义了一个事务代理。如果愿意这个代理所暴露的实际接口也可以显式定义。默认情况下,所有由目标对象实现的接口都暴露出来,在这个例子中就是应用的Clinic服务接口。

从客户端的观点来看,"clinic" bean只是这个应用的Clinic接口的实现。客户端不需要知道这会被一个事务代理所处理。这就是接口的能力:一个直接的目标对象的引用可以容易的被一个实现相同接口的代理所代替—在这儿就是一个隐式创建事务的代理。

代理的具体事务行为会由为根据特定的方法或方法命名模式而定义的事务属性来驱动,就像下面的例子所示:

清单3

<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>

Key属性决定代理将为方法提供什么样的事务行为。这个属性的最重要部分就是事务传播行为。下面是一些可选的属性值

  1. PROPAGATION_REQUIRED --支持当前的事务,如果不存在就创建一个新的。这是最常用的选择。
  2. PROPAGATION_SUPPORTS --支持当前的事务,如果不存在就不使用事务。
  3. PROPAGATION_MANDATORY --支持当前的事务,如果不存在就抛出异常。
  4. PROPAGATION_REQUIRES_NEW --创建一个新的事务,并暂停当前的事务(如果存在)。
  5. PROPAGATION_NOT_SUPPORTED --不使用事务,并暂停当前的事务(如果存在)。
  6. PROPAGATION_NEVER --不使用事务,如果当前存在事务就抛出异常。
  7. PROPAGATION_NESTED --如果当前存在事务就作为嵌入事务执行,否则与PROPAGATION_REQUIRED类似。

前6个事务策略与EJB的CMT类似,而且使用相同的常量名,因此对EJB开发人员来说是很亲切的。第7个策略PROPAGATION_NESTED是Spring提供的一个变体:他需要事务管理器(如DataSourceTransactionManager)提供类似JDBC3.0那样的保存点API来嵌套事务行为或者通过

JTA支持嵌套事务。

事务属性中的readOnly标识指示相应的事务应该作为一个只读事务来优化。这是一个优化提示:一些事务策略在这种情况下可以得到很好的性能优化,如使用ORM工具如Hibernate或TopLink时避免脏数据检查(“flush”尝试)。

在事务属性中还有一个“timeout”选项来定义事务的超时秒数。在JTA中,这个属性会简单地传递给J2EE服务器的事务协调器并被正确地解释。

使用事务代理

在运行时,客户端会取得一个“clinic”引用并转换为Clinic接口,然后调用如loadPet或storePet方法。这就隐式地使用了Spring的事务代理,通过“事务解释器”在目标对象中注册;这样一个新的事务就创建了,然后具体的工作就会代理给JdbcClinic的目标方法。

图1示例了一个使用“建议链”并到达最后目标的AOP代理的潜在概念。在这个示例中,唯一的建议是一个事务解释器用来包装目标方法的事务行为。这是一种用来在声明性事务功能下使用的基于代理的AOP。

详解阿里P7架构师是怎么在Spring中实现事务暂停

 

Figure 1. An AOP proxy with an advisor chain and a target at the end

例如,一个PetClinic应用的WEB层组件可以执行ServletContext定位来获取Spring WebApplicationContext的引用并且获取受管理的“clinic”BEAN:

清单4

WebApplicationContext ctx = 
 WebApplicationContexUtils.getWebApplicationContext(servletContext);
Clinic clinic = (Clinic) ctx.getBean("clinic);
Pet pet = new Pet();
pet.setName("my new cat");
clinic.storePet(pet);

在调用storePet()之前,Spring的事务代理隐式地创建一个事务。当storePet()调用返回时,事务将提交或回滚。缺省情况下任何RuntimeException或Error将导致回滚。实际的提交或回滚可以是可以定义的:Spring的事务属性支持“回滚规则”的概念。

例如,我们可以可以引入一个强制的PetClinicException并且告诉事务代理在抛出异常时回滚:

清单5

<prop key="load*">PROPAGATION_REQUIRED,readOnly,-PetClinicException</prop>
<prop key="store*">PROPAGATION_REQUIRED,-PetClinicException</prop>

这儿也有一个类似的“提交规则”语法,指示特定的异常将触发一次提交。

注意上面示例的显式定位引用的方法只是一种访问受Spring管理BEAN的方法的变化,可以用在任何WEB资源如servlet或filter。在构建基于Spring自身的MVC框架时,BEAN可以直接被注射到WEB控制器中。当然也支持在如Struts, WebWork, JSF, and Tapestry框架中访问Spring管理BEAN。详情可以参考Spring的文档。

详解阿里P7架构师是怎么在Spring中实现事务暂停

 

PlatformTransactionManager策略

Spring事务支持的核心接口是org.springframework.transaction.PlatformTransactionManager。所有Spring的事务分离功能都会委托给PlatformTransactionManager(传给相应的TransactionDefinition实例)来做实际的事务执行。虽然PlatformTransactionManager接口可以直接调用,但通常应用只需要配置一个具体的事务管理器并且通过声明性事务来分离事务。

Spring提供几种不同的PlatformTransactionManager实现,分为如下两个类别:

  1. 本地事务策略—支持单一资源的事务(通常是单个数据库),其包括org.springframework.jdbc.datasource.DataSourceTransactionManager和 org.springframework.orm.hibernate.HibernateTransactionManager。
  2. 全局事务管理—支持可能跨越多个资源的全局事务。其相应的类为org.springframework.transaction.jta.JtaTransactionManager,将事务委托给遵循JTA规范的事务协调器(通常为J2EE服务器,但不是强制的)。

PlatformTransactionManager抽象的主要价值在于应用不再被绑定在特定的事务管理环境。相反,事务策略可以很容易地切换—通过选择不同的PlatformTransactionManager实现类。这就使得应用代码与声明事务分离保持一致,而不需要考虑应用组件所使用的环境了。

例如,应用的初始版本可能布署在Tomcat上,与单个Oracle数据库交互。这可以方便地利用Spring的事务分离特性,只要选择基于JDBC的DataSourceTransactionManager作为使用的事务策略。Spring会分离事务,而JDBC驱动会执行相应的原始JDBC事务。

相同应用的另一个版本可能会布署在WebLogic服务器上,使用两个Oracle数据库。应用代码和事务分离不需要改变。唯一不同的是选择作为JtaTransactionManager事务策略,让Spring来分离事务而WebLogic服务器的事务协调器来执行事务。

JTA UserTransaction与JTA TransactionManager比较

让我们来看一下Spring对JTA支持的细节。虽然并非经常需要考虑这个细节但了解相关的细节还有必要的。对简单的用例如前面章节的示例,标准的JtaTransactionManager定义已经足够了,缺省的Spring JtaTransactionManager设置会从标准JNDI位置(J2EE规范所定义的java:comp/UserTransaction)获取JTA的javax.transaction.UserTransaction对象。这对大部分标准J2EE环境来说已经足够了。

然而,缺省的JtaTransactionManager不能执行事务暂停(也就是说不支持PROPAGATION_REQUIRES_NEW和PROPAGATION_NOT_SUPPORTED)。原因就在于标准的JTA UserTransaction接口不支持事务的暂停和恢复,而只支持开始和完成新的事务。

为了实现事务的暂停,需要一个javax.transaction.TransactionManager实例,他提供了JTA定义的标准的暂停和恢复方法。不幸的是,J2EE没有为JTA TransactionManager定义标准的JNDI位置!因此,我们需要使用厂商自己的定位机制。

清单6

<bean id="transactionManager" 
 class="org.springframework.transaction.jta.JtaTransactionManager">
 <property name="transactionManagerName">
 <value>vendorSpecificJndiLocation</value>
 </property>
</bean>

J2EE本质上没有考虑将JTA TransactionManager接口作为公共API的一部分。JTA规范自身定义了将TransactionManager接口作为容器集成的想法。虽然这是可以理解的,但是JTA TransactionManager的标准JNDI位置还是可以增加一定的价值,特别是对轻量级容器如Spring,这样任何J2EE服务器就可以用统一的方式来定位JTA TransactionManager了。

不仅Spring的JtaTransactionManager可以从访问中获益,O/R映射工具如Hibernate, Apache OJB, and Kodo JDO也能得到好处,因为他们需要在JTA环境中执行缓存同步的能力(释放缓存意味着JTA事务的完成)。这种注册事务同步的能力只有JTA TransactionManager接口才能提供,而UserTransaction是处理不了的。因此,这些工具都需要实现自己的TransactionManager定位器。

为JTA TransactionManager定义标准的JNDI位置是许多底层软件供应商最期望J2EE实现的功能。如果J2EE5.0的规范制定团队能够认识到这个特性的重要性就太好了。幸运地是,高级J2EE服务器如WebLogic Server已经考虑将JTA TransactionManager作为公共的API包含在扩展功能中。

在WebLogic JTA中实现Spring的事务分离

在WebLogic Server中,JTA TransactionManager官方的JNDI位置定义为javax.transaction.TransactionManager。这个值可以在Spring的JtaTransactionManager中作为“transactionManagerName”使用。原则上这样就可以在WebLogic's JTA系统中实现事务暂停了,也就是说支持PROPAGATION_REQUIRES_NEW和PROPAGATION_NOT_SUPPORTED行为。

除了标准的JtaTransactionManager和其支持的通用配置选项外,Spring还提供了一个专用的WebLogicJtaTransactionManager适配器来直接利用WebLogic的JTA扩展。

在享受自动探测WebLogic的JTA TransactionManager的便利之外,他提供超越标准JTA的三个重要特性:

  1. 事务命名—暴露出Spring的事务名给WebLogic Server,使得Spring事务在WebLogic的事务监听器可见。缺省的,Spring会使用声明性事务的完整方法名。
  2. 每事务隔离级别—将Spring事务属性中定义的隔离级别应用到WebLogic JTA事务中。这使得每个事务都可以定义数据库的隔离级别,而这是标准JTA所不支持的。
  3. 强制事务恢复—即使在暂停的事务被标识为回滚时也可以恢复。这需要使用WebLogic的扩展TransactionManager接口来调用forceResume()方法。
详解阿里P7架构师是怎么在Spring中实现事务暂停

 

Figure 2. WebLogic Server's transaction monitor (click the image for a full-size screen shot)

Spring的WebLogicJtaTransactionManager有效地为基于Spring的应用提供了WebLogic Server事务管理的全部功能。这使得Spring事务分离成为一种能与EJB CMT竟争的产品,而且提供了相同级别的事务支持。

Spring and EJB CMT

如上所示,Spring的POJO声明性事务分离可以作为一种除传统EJB CMT这外的选择。但是Spring与EJB并不是完成互斥的,Spring的应用上下文也可以作为EJB façade的后台来管理数据访问(DAO)和其他细纹理的业务对象。

在EJB情景中,事务是由EJB CMT来驱动的。对Spring来说,数据访问支持特性会自动检测到这样的环境并且采用相应的事务。例如,Spring对Hibernate的支持能够提供隐式的资源管理,即使是EJB驱动的事务,甚至可以在不需要修改任何DAO代码的情况下提供相同的语义。

Spring有效的解耦了DAO实现与实际的运行环境。DAO可以参与Spring的事务就像参与EJB CMT事务一样。这不仅简化在其他环境中的重用,而且更方便在J2EE容器外进行测试。

 

结论

Spring框架为J2EE和非J2EE环境提供了全量的事务分离的特性,特别表现在POJO的声明性事务上。他用一种灵活而非侵入式的方式为非EJB环境中的事务分离提供了便利。与EJB不同,这样的事务性POJO应用对象可以很容易的被测试和在J2EE容器外补重用。

Spring提供了各种事务策略,如JtaTransactionManager是用来代理J2EE服务器的事务协调器,而JDBC DataSourceTransactionManager是用来为简单的JDBC DataSource(就是单一目标数据库)执行事务。Spring可以很容易为不同的环境通过后台配置的简单修改来调整事务策略。

超越标准的JTA支持,Spring为WebLogic Server的JTA扩展提供了完善的集成,可以支持高级特性如事务监视和每事务隔离级别。通过对WebLogic Server的特殊支持,基于Spring的应用可以完全利用WebLogic Server的事务管理功能。

Spring事务分离是继EJB CMT之外的另一种可选方式,特别是对那些基于POJO的轻量级架构。在那只是因为选择LSSB(本地无状态会话BEAN)来应用声明性事务的情况下,基于Spring的POJO服务模型是一种可行的选择,他提供了非常高层的灵活性、可测试性和重用性。

Java肖先生:专注于Java开发技术的研究与知识分享!



Tags:Spring   点击:()  评论:()
声明:本站部分内容来自互联网,内容观点仅代表作者本人,如有任何版权侵犯请与我们联系,我们将立即删除。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
在我们实际工作中,总会遇到这样需求,在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等。今天就给大家介绍一个 Spring Boot 神器,专门帮助大家解...【详细内容】
2019-12-26   Spring  点击:(1)  评论:(0)  加入收藏
如果这两天登录 https://start.spring.io/ 就会发现,Spring Boot 默认版本已经升到了 2.1.0。这是因为 Spring Boot 刚刚发布了 2.1.0 版本,我们来看下 Spring Boot 2 发布以...【详细内容】
2019-12-26   Spring  点击:(3)  评论:(0)  加入收藏
一、什么是Spring Security?Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,它是用于保护基于Spring的应用程序的实际标准。Spring Security是一个框架,...【详细内容】
2019-12-25   Spring  点击:(4)  评论:(0)  加入收藏
这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例。先和大家聊聊我为什么喜欢写这种脚手架的项目,在我学习一门新技术的时候,总是想快速的搭建起一个 Demo 来试试...【详细内容】
2019-12-25   Spring  点击:(1)  评论:(0)  加入收藏
使用了注解的方式进行对接口防刷的功能,非常高大上,本文章仅供参考 一,技术要点:springboot的基本知识,redis基本操作,...【详细内容】
2019-12-25   Spring  点击:(2)  评论:(0)  加入收藏
在SpringBoot的Web项目中,默认采用的是内置Tomcat,当然也可以配置支持内置的jetty,内置有什么好处呢?...【详细内容】
2019-12-01   Spring  点击:(3)  评论:(0)  加入收藏
一、注解(annotations)列表@SpringBootApplication:包含了@ComponentScan、@Configuration和@EnableAutoConfiguration注解。其中@ComponentScan让spring Boot扫描到Configur...【详细内容】
2019-12-23   Spring  点击:(7)  评论:(0)  加入收藏
前言前面一篇文章我讲了一下Web应用程序的基础,主要是基于网络连接的I/O数据处理,为了对网络上传输的数据进行处理,我们首先必须清楚的了解网络传输的数据是二进制数据流由...【详细内容】
2019-12-23   Spring  点击:(5)  评论:(0)  加入收藏
升级 https 记录1、去阿里云购买证书(免费版),并提交审核资料 购买的证书2、下载证书 下载证书3、查看上图页面的第三步 JKS证书安装4、在证书目录下执行阿里云提供的命令,密码...【详细内容】
2019-12-23   Spring  点击:(12)  评论:(0)  加入收藏
小编2015年进入一家互联网公司开始接触微服务。当时对微服务的概念也是懵懵懂懂。在逐步的探索中,踩了大大小小的坑不计其数。最开始使用的还是Springboot1.4.x的版本。版本...【详细内容】
2019-12-19   Spring  点击:(8)  评论:(0)  加入收藏
本篇主要讲述如何使用基本的注解 @Cacheable @CachePut @CacheEvict 操作缓存1.我们导入Redis的依赖<!--这里Redis我给了版本--> <dependency> <groupId>org.springframewor...【详细内容】
2019-12-18   Spring  点击:(9)  评论:(0)  加入收藏
1、Log4j2优点具体优点可以参考官方文档:https://logging.apache.org/log4j/2.x/我这边只简单说一下:相比与其他的日志系统,log4j2丢数据的情况少;在多线程环境下,性能高于logbac...【详细内容】
2019-12-17   Spring  点击:(10)  评论:(0)  加入收藏
想说说自己Spring的学习路程,课余自学Spring将近一年了,还是不得其道。去年暑假学习了一下JSP,并没有深入理解,所以导致学习Spring时对着书本写一些demo,感觉自己理解了,其实并不...【详细内容】
2019-09-29   Spring  点击:(5)  评论:(0)  加入收藏
1. 概述分析源码是一件非常具有挑战性的工作,在正是分析spring的源码之前我们先来简单回顾下spring核心功能的简单使用2. 容器的基本用法bean是spring最核心的东西,spring就像...【详细内容】
2019-12-13   Spring  点击:(9)  评论:(0)  加入收藏
在使用Mybatis Plus的雪花算法生成Long类型id时发现Swagger返回id与数据库的id不一致,但直接访问URL接口时返回的id却是正确的,即数据库id与URL访问返回的id一致,仅Swagger不一...【详细内容】
2019-12-13   Spring  点击:(11)  评论:(0)  加入收藏
在使用SSM整合的时候,spring mvc 添加@ResponseBody的时候,正常情况下都会返回json的。但是又的时候如果没有配置好的话,如果想要返回Map的json对象会报:No converter found for...【详细内容】
2019-12-13   Spring  点击:(8)  评论:(0)  加入收藏
什么是Spring Boot框架?什么是Spring框架?他们的目标是什么?他们产生的背景是怎样的,两者有什么区别,我们应该使用哪个框架进行开发,带着这样的疑问,我们开始今天的讲解。 Spring...【详细内容】
2019-12-09   Spring  点击:(11)  评论:(0)  加入收藏
1、存放目录Application属性文件,按优先级排序,位置高的将覆盖位置当前项目目录下的一个/config子目录当前项目目录项目的resources即一个classpath下的/config包项目的resour...【详细内容】
2019-11-21   Spring  点击:(71)  评论:(0)  加入收藏
一、Spring Cloud是什么?能做什么?Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消...【详细内容】
2019-11-20   Spring  点击:(9)  评论:(0)  加入收藏
关于Docsify官网地址:https://docsify.js.org/在Docsify官网对Docsify是这样的描述的:docsify 是一个动态生成文档网站的工具。不同于 GitBook、Hexo 的地方是它不会生成将...【详细内容】
2019-11-11   Spring  点击:(38)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条