> 文章列表 > 分析Spring事务管理原理及应用

分析Spring事务管理原理及应用

分析Spring事务管理原理及应用

目录

一、Spring事务管理介绍

(一)基本理论

(二)实际工作中的举例

(三)简单应用举例

二、Spring事务配置介绍

(一)Spring事务属性介绍

 传播属性(传播行为)可选值说明

(二)声明式事务配置示例

基于XML的配置

基于注解的配置

三、分析Spring事务实现原理

(一)整体流程梳理

 (二)核心实现分析

annotation-driven 解析

代理类创建分析

寻找合适通知器的时序图展示

InfrastructureAdvisorAutoProxyCreator处理器注册与实例化展示

为使用事务的目标类生成代理的时序图展示

事务处理分析

 四、总结

参考文献


一、Spring事务管理介绍

(一)基本理论

在软件开发中,事务管理是一种处理数据库操作的方式,它可以确保一组数据库操作要么全部成功执行,要么全部失败回滚,从而保持数据的一致性和完整性

Spring框架是一个流行的Java企业级应用程序开发框架,提供了强大的事务管理功能,可以轻松地在Spring应用程序中管理数据库事务。Spring事务管理通过提供一种声明性事务管理的方式,将事务管理从业务逻辑中解耦,并通过使用AOP(面向切面编程)技术实现事务管理。Spring框架支持多种事务管理策略,包括本地事务和分布式事务。

Spring事务管理的一些核心概念和功能包括:

  1. 事务管理器(Transaction Manager):负责管理数据库事务的对象。Spring支持多种事务管理器,如JDBC事务管理器、Hibernate事务管理器、JTA事务管理器等。
  2. 事务传播行为(Transaction Propagation):定义了在多个事务方法相互调用时,事务如何传播的规则。例如,一个事务方法A调用了另一个事务方法B,事务传播行为定义了B方法是加入A方法的事务,还是创建一个新的事务。
  3. 事务隔离级别(Transaction Isolation Level):定义了事务对数据库的隔离程度。不同的隔离级别提供了不同的并发控制策略,例如读未提交、读已提交、可重复读和串行化。
  4. 事务回滚(Transaction Rollback):当事务操作失败时,事务管理器可以自动回滚所有已执行的数据库操作,从而将数据库状态恢复到事务开始前的状态。
  5. 声明式事务管理(Declarative Transaction Management):通过在方法或类级别上使用Spring的事务管理注解,如@Transactional,可以将事务管理规则与业务逻辑解耦。这样,开发人员可以在代码中专注于业务逻辑,而不需要显式地编写事务管理代码。
  6. 编程式事务管理(Programmatic Transaction Management):可以使用编程方式通过编写事务管理代码来管理事务,例如使用Spring的TransactionTemplate API。

Spring事务管理可以应用于多种数据访问技术,如JDBC、Hibernate、JPA等,并且可以与其他Spring特性,如Spring的AOP和Spring的IoC容器一起使用,从而提供了一个强大的事务管理解决方案,使开发人员能够轻松地实现事务管理,并确保数据的一致性和完整性。

(二)实际工作中的举例

在实际工作中,Spring事务管理广泛应用于各种Java企业级应用程序中,包括Web应用、后端服务、批处理作业等。以下是一些使用Spring事务管理的实际应用举例:

  1. 电商应用:在一个电商应用中,订单管理涉及到对多个数据库表的增、删、改等操作,例如创建订单、更新库存、扣减账户余额等。使用Spring事务管理,可以确保这些数据库操作要么全部成功提交,要么全部回滚,从而保持订单和库存、账户之间的一致性。
  2. 银行系统:在一个银行系统中,涉及到对账户的转账、存款、取款等操作,这些操作必须保证在一个事务中进行,以确保资金的正确处理。使用Spring事务管理,可以将这些操作封装在一个事务中,从而保持账户操作的一致性和完整性。
  3. 社交媒体应用:在一个社交媒体应用中,用户可能同时进行多个操作,例如发布帖子、评论、点赞等。使用Spring事务管理,可以将这些操作组织成一个事务,从而保持数据的一致性,例如在发布帖子时,同时插入帖子信息和更新用户的动态信息。
  4. 后端服务:在后端服务中,可能会涉及到多个数据库操作,例如从多个数据源中读取数据、更新多个数据库表等。使用Spring事务管理,可以将这些操作组织成一个事务,从而保证数据操作的一致性。
  5. 批处理作业:在批处理作业中,可能需要对大量数据进行处理,包括从文件读取数据、处理数据、将结果写入数据库等。使用Spring事务管理,可以将这些操作封装在一个事务中,从而确保数据处理的一致性和完整性。

这些只是一些应用Spring事务管理的实际举例,实际上,在许多复杂的应用中,使用Spring事务管理可以有效地管理数据库事务,确保数据的一致性和完整性,并提供良好的容错能力和可靠性。

(三)简单应用举例

当使用Spring事务管理时,通常需要通过配置事务管理器、事务切面等来实现。下面是一个简单的Java代码示例,演示了如何在Spring中配置和使用事务管理。

假设有一个简单的订单管理系统,包括订单Service和订单DAO两个类,其中订单Service负责处理订单的业务逻辑,订单DAO负责与数据库进行交互。在订单Service中,我们希望对创建订单和更新订单状态两个操作进行事务管理。

首先,在Spring的配置文件中配置事务管理器和事务切面,例如使用Spring的声明式事务管理:

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" />
</bean><!-- 配置事务切面 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!-- 配置需要进行事务管理的方法 --><tx:method name="createOrder" propagation="REQUIRED" /><tx:method name="updateOrderStatus" propagation="REQUIRED" /></tx:attributes>
</tx:advice><!-- 配置AOP代理 -->
<aop:config><aop:pointcut id="txPointcut" expression="execution(* com.example.order.service..*.*(..))" /><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>

接下来,在订单Service中使用@Transactional注解标记需要进行事务管理的方法,例如:

@Service
public class OrderService {@Autowiredprivate OrderDao orderDao;@Transactionalpublic void createOrder(Order order) {// 创建订单的业务逻辑orderDao.createOrder(order);}@Transactionalpublic void updateOrderStatus(Long orderId, String status) {// 更新订单状态的业务逻辑orderDao.updateOrderStatus(orderId, status);}
}

在上面的例子中,createOrder和updateOrderStatus方法都被标记为@Transactional,表示它们需要在事务中进行操作。当这两个方法被调用时,Spring会自动开启一个事务,并在方法执行完成后自动提交事务,或者在方法发生异常时自动回滚事务。

这样,当调用OrderService的createOrder和updateOrderStatus方法时,会自动启用Spring事务管理,确保订单创建和订单状态更新在一个事务中进行,保障数据的一致性。

二、Spring事务配置介绍

Spring的使用方式有两种,即通常所说的编程式事务和声明式事务

  • 编程式事务是指应用通过使用spring提供的各个事务相关的类,通过编写代码来完成事务的设置,这种的使用门槛较高,使用难度稍大一些,而且不方便,大多数时候我们并不会用到。
  • 声明式事务则指通过配置的形式来引入spring的事务管理,具体的配置方式又可以分为通过XML文件配置和通过注解配置。由于配置方式上手容易,需要配置的内容也不多,尤其是基于注解的配置,已经成了目前引入事务的首选

(一)Spring事务属性介绍

 传播属性(传播行为)可选值说明

枚举值 变量名 说明
0 PROPAGATION_REQUIRED 当前方法必须运行在事务中。如果当前有事务,则方法将会在该事务中运行。否则就启动一个新的事务
1 PROPAGATION_SUPPORTS 当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
2 PROPAGATION_MANDATORY 该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
3 PROPAGATION_REQUIRES_NEW 当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果当前有事务则会被挂起
4 PROPAGATION_NOT_SUPPORTED 该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。
5 PROPAGATION_NEVER 当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常,这和2正好相反
6 PROPAGATION_NESTED 如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。

(二)声明式事务配置示例

Spring事务管理组件主要由数据源,事务管理器和代理类这三部分组成,可以用下图来表示。

 其中数据源是指定数据库的连接方式,而事务管理器则定义了使用哪一类事务管理器,不同的数据库中间件其实现得也不一样,而代理类则表示哪些类的哪些方法需要用到事务。

Spring对主流数据库都有支持,我们以mybatis为例,分别介绍基于配置文件和基于注解的配置。假设我们需要给一个org.zyf.keep.crm.service目录下的所有Service的所有update方法和insert方法加上事务,两种配置方式分别如下:

基于XML的配置

基于XML的配置方式也有很多种,这里选择最精简的基于tx标签的配置,核心配置示例如下:

<!--1. 定义数据源-->
<bean id="dataSource" class="xxx.DataSource" />
<!--2. 定义事务管理器 -->
<bean name="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" />
</bean>
<!--3. 定义事务切面 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="*" propagation="REQUIRED" /></tx:attributes></tx:advice>
<!--3. 定义切点 --><aop:config><aop:pointcut id="pointsCut" expression="execution(* org.zyf.keep.crm.service.*Service.update*(..))" /><aop:advisor advice-ref="txAdvice" pointcut-ref="pointsCut" />       
</aop:config> 

基于注解的配置

主要配置如下:

<!--1. 定义数据源-->
<bean id="dataSource" class="xxx.DataSource" />
<!--2. 定义事务管理器 -->
<bean name="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" />
</bean>
<!--3. 配置注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

这种情况下,我们需要对使用注解的Service,在方法上或者是在类上加上注解,示例如下:

@Service
public class UserServiceImpl implements UserService {@Transactionalpublic void update(UserDO user){//....}
}

对于注解@Transactional来说,可以不配置任何属性,也可以显示指定某些属性,该注解的属性定义如下:

public @interface Transactional {@AliasFor("transactionManager")String value() default ""; // transactionManager 的别名@AliasFor("value")String transactionManager() default ""; //value的别名Propagation propagation() default Propagation.REQUIRED; //传播行为Isolation isolation() default Isolation.DEFAULT; //隔离级别,数据库默认int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;//超时时间,数据库默认boolean readOnly() default false; //是否为只读事务,默认非只读Class<? extends Throwable>[] rollbackFor() default {}; //需要回滚的异常类String[] rollbackForClassName() default {}; //需要回滚的异常类名Class<? extends Throwable>[] noRollbackFor() default {};//不回滚的异常类String[] noRollbackForClassName() default {};//不回滚的异常类名
}
​

对于这个注解@Transactional来说,我们可以不配置任何属性,也可以显示指定某些属性,该注解的属性定义如下:

三、分析Spring事务实现原理

通过配置已经大体知道了spring事务管理实现的原理就是aop,不难推测,spring会提供实现事务管理相关功能的切面,切点和通知相关的类,来完成对于事务相关功能的支持。

我们以配置最简单的注解式配置为例,分析其实现方式。

(一)整体流程梳理

对于通过spring管理的业务方法来说,其处理流程大概如下,重点关注的是其配置解析,事务创建及提交回滚等相关逻辑。

 (二)核心实现分析

只对基于注解的实现方式进行分析,其它的方式大同小异,核心思想都是一致的,即通过AOP机制来创建为目标类应用上事务切面。基于注解的关键配置有两个,一个是<tx:annotation-driven transaction-manager="transactionManager"/>,另一个则是@Transactional。

annotation-driven 解析

annotation-driven 是自定义标签tx的属性,这里用到了spring的命名空间机制,关于命名空间机制我们这里不展开说,只需要记住,对于每一个自定义标签,需要有一个标签处理器即可,而这个标签处理器也不需要做太多的工作,因为spring已经提供了一个NamespaceHandlerSupport,实现了大部分功能,而自定义的处理类只需要实现init方法即可,tx对应的标签处理器是 TxNamespaceHandler,其init方法定义如下:

/** Copyright 2002-2012 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.transaction.config;import org.w3c.dom.Element;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;/*** {@code NamespaceHandler} allowing for the configuration of* declarative transaction management using either XML or using annotations.** <p>This namespace handler is the central piece of functionality in the* Spring transaction management facilities and offers two approaches* to declaratively manage transactions.** <p>One approach uses transaction semantics defined in XML using the* {@code <tx:advice>} elements, the other uses annotations* in combination with the {@code <tx:annotation-driven>} element.* Both approached are detailed to great extent in the Spring reference manual.** @author Rob Harrop* @author Juergen Hoeller* @since 2.0*/
public class TxNamespaceHandler extends NamespaceHandlerSupport {static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}}

可见init方法主要是注册了一些bean解析器,用于解析这个标签上的一些属性。标签annotation-driven 是由 AnnotationDrivenBeanDefinitionParser 来解析的,其解析方法如下:

/** Copyright 2002-2015 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.transaction.config;import org.w3c.dom.Element;import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.transaction.event.TransactionalEventListenerFactory;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;/*** {@link org.springframework.beans.factory.xml.BeanDefinitionParser* BeanDefinitionParser} implementation that allows users to easily configure* all the infrastructure beans required to enable annotation-driven transaction* demarcation.** <p>By default, all proxies are created as JDK proxies. This may cause some* problems if you are injecting objects as concrete classes rather than* interfaces. To overcome this restriction you can set the* '{@code proxy-target-class}' attribute to '{@code true}', which* will result in class-based proxies being created.** @author Juergen Hoeller* @author Rob Harrop* @author Chris Beams* @author Stephane Nicoll* @since 2.0*/
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {/*** Parses the {@code <tx:annotation-driven/>} tag. Will* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}* with the container as necessary.*/@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj"registerTransactionAspect(element, parserContext);}else {// mode="proxy"AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}private void registerTransactionAspect(Element element, ParserContext parserContext) {String txAspectBeanName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME;String txAspectClassName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_CLASS_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAspectBeanName)) {RootBeanDefinition def = new RootBeanDefinition();def.setBeanClassName(txAspectClassName);def.setFactoryMethodName("aspectOf");registerTransactionManager(element, def);parserContext.registerBeanComponent(new BeanComponentDefinition(def, txAspectBeanName));}}private static void registerTransactionManager(Element element, BeanDefinition def) {def.getPropertyValues().add("transactionManagerBeanName",TxNamespaceHandler.getTransactionManagerName(element));}private void registerTransactionalEventListenerFactory(ParserContext parserContext) {RootBeanDefinition def = new RootBeanDefinition();def.setBeanClass(TransactionalEventListenerFactory.class);parserContext.registerBeanComponent(new BeanComponentDefinition(def,TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME));}/*** Inner class to just introduce an AOP framework dependency when actually in proxy mode.*/private static class AopAutoProxyConfigurer {public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {//1. 在必要的时候注册 InfrastructureAdvisorAutoProxyCreator,默认情况下,会注册的AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {Object eleSource = parserContext.extractSource(element);// Create the TransactionAttributeSource definition.RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// Create the TransactionInterceptor definition.RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registerTransactionManager(element, interceptorDef);interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);// Create the TransactionAttributeSourceAdvisor definition.RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);if (element.hasAttribute("order")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));parserContext.registerComponent(compositeDef);}}}}

这段代码的主要作用就是注册了4个类。这4个类的主要作用如下:

  • InfrastructureAdvisorAutoProxyCreator:实现了 BeanPostProcessor 接口,在 postProcessAfterInitialization 方法中会根据需要创建 Proxy 类。
  • AnnotationTransactionAttributeSource:解析事务类,得到事务配置相关信息。
  • TransactionInterceptor:事务拦截器,实现了 Advice、MethodInterceptor 接口。其属性transactionAttributeSource 引用了AnnotationTransactionAttributeSource 的实例。
  • BeanFactoryTransactionAttributeSourceAdvisor:实现了 PointcutAdvisor 接口,同时其两个属性transactionAttributeSourceadviceBeanName 分别引用了AnnotationTransactionAttributeSourceTransactionInterceptor

annotation-driven 解析简单小结

  1. tx 是一个自定义标签,由一个对应的命名处理器 TxNamespaceHandler 来实现
  2. TxNamespaceHandler 在初始化是地,为 annotation-driven 注册了一个对应的解析器 AnnotationDrivenBeanDefinitionParser
  3. 该解析器在解析的时候,注册了4个和事务管理相关的类。

代理类创建分析

对于spring 事务管理来说,也是通过切面的方式来实现,所以它符合AOP主流程,利用这个主流程,我们需要有一个Advisior的查找类,这个需要继承自 AbstractAdvisiorAutoProxyCreator,因为前者是一个抽象类。另外,需要有对应的切面(实现Advisior或者是有@Aspect注解),这个切面spring并没有让用户自行配置,所以只能是由它来提供。如果能找到这样的切面,那么就可以进行代理类的创建。否则就创建不了。

那么Spring 有没有提供,以及是如何提供的呢,答案是肯定的,提供的方式就在于上面我们分析的annotation-driven 里。前面说到解析时注册了4个类的实例,其中InfrastructureAdvisorAutoProxyCreator 正是实现了AbstractAdvisiorAutoProxyCreator接口,而BeanFactoryTransactionAttributeSourceAdvisor则正是一个切面,其类层次结构如下:

我们在开发代码中只有加了@Transactional注解的类或方法才需要被代理,这是因为事实上,在findEligibleAdvisors 方法的实现上,第一步是查找匹配的Advisors,第二步还要对第一步的结果进行过滤,过滤的逻辑就是只保留那些适合于当前bean的切面:

  • BeanFactoryTransactionAttributeSourceAdvisor 内部维护限一个Pointcut(TransactionAttributeSourcePointcut )和一个事务属性transactionAttributeSource), 并把判断的逻辑交给了这个Pointcut
  • TransactionAttributeSourcePointcut 的match方法里,其判断逻辑是是否能从类或属性上找到trransactionAttributeSource 资源
  • AnnotationTransactionAttributeSource 会将具体的查找逻辑交给 TransactionAnnotationParser 去处理(SpringTransactionAnnotationParser)
  • 最终由 SpringTransactionAnnotationParser 完成最后的处理

相关代码如下:

@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {AnnotationAttributes attributes =  AnnotatedElementUtils.getMergedAnnotationAttributes(ae,         Transactional.classTransactional.class);if (attributes != null) {return parseTransactionAnnotation(attributes);}
}
//处理注解上的一些属性
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();Propagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());// 省略一部分rbta.getRollbackRules().addAll(rollBackRules);return rbta;
}

从代码实现上来看,判断标准就是看是否有Transactional 注解。

寻找合适通知器的时序图展示

image.png

InfrastructureAdvisorAutoProxyCreator处理器注册与实例化展示

image.png

为使用事务的目标类生成代理的时序图展示

image.png

 至此,我们就分析完了spring事务切面查询与匹配校验,最终得到的切面列表,如果不为空的话,则会作为创建代理类的参数之一,由AbstractAutoProxyCreator 来完成 最终代理类的创建,由于这个是spring aop本身的逻辑,已经与事务无关,所以就不再继续分析了。总结如下:

  1. 创建代理类的关键在于是否能查找到匹配的切面
  2. spring 在解析 annotation-driven 时,就自动注册了4个和事务相关的类的实体,其中包括切面查找类和切面类的实例
  3. 查找切面之后,还需要对切面进行过滤,以判断是否能够关联切面,该过滤方法经层层委托,最后由一个注解处理器来实现,其实现标准就是看类或方法上是否有Transactional 注解
  4. 通过上述流程,可以保证给且只给带Transactional 注解的类创建代理类。

事务处理分析

分析一下spring创建 的代理类是怎么让事务生效的。

对于创建的代理类来说,其切面就是BeanFactoryTransactionAttributeSourceAdvisor的实例,根据该类的定义,其切点很明显就是TransactionAttributeSourcePointcut,而TransactionInterceptor,则充当了通知的角色,一方面advisior在实例化的时候就引用了这个对象,并作为通知 来使用。另外一方面,这个类确实也是一个Advice,其类结构如下:

这个实现了Advice接口,用于对业务类进行业务增强。这个会在创建代理类的时候进行引入。真正在进行代理类方法调用时,会调用其invoke方法,最终会调用到invokeWithinTransaction方法,其主要逻辑如下:

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
​// 1. 获取并解析当时设置的事务配置属性,这个属性由方法上的注解来设置final TransactionAttribute txAttr =     getTransactionAttributeSource().getTransactionAttribute(method, targetClass);//2. 获取平台相关的事务管理器,这个需要手动在xml文件里配置
final PlatformTransactionManager tm = determineTransactionManager(txAttr);//3. 获取连接点
final String joinpointIdentification = methodIdentification(method, targetClass);
​if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// 一般来说,平台的事务管理器都不会继承自CallbackPreferringPlatformTransactionManager,所以这个分支会走到。//4. 创建事务对象,开启事务等一系列操作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;
}
​

上面的代码描述了事务处理的主流程,在上面的几个步骤里,事务的提交是比较简单的,事务的创建和事务的撤销,由于加入了spring管理自身的一些元素,所以相对要复杂一些,这里也进行简单的介绍。

整体总结如下:

image.png

 四、总结

Spring事务能力的支撑用到了很多知识,动态代理、AOP、反射、后置处理器等等,总的来说就是应用启动时为需要使用事务的类生成代理类,以及将事务能力(拦截逻辑)织入进去,在实例化的时候调用后置处理器的逻辑,将代理类实例化替代目标类,并放入上下文容器中,在实际调用目标类事务方法的时候,被代理类中ReflectiveMethodInvocation拦截,然后先调用拦截器中的事务逻辑,然后再调用目标类的业务逻辑,最后处理异常回滚和提交,看起来比较简单,但是框架层面提供了非常庞大的基础组件来支撑和实现事务能力,当然这些基础组件大部分都会复用,比如AOP和动态代理,在异步和缓存场景下都会用到,包括我们自己扩展一些能力出来的时候也会用到。
 

参考文献

1.Spring事务原理详解_叔牙的博客-CSDN博客