首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-02 12:43
  • 2935
  • 9642
blog.csdn.net

目录

一、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的声明式事务管理:

  1. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <property name="dataSource" ref="dataSource" />
  3. bean>
  4. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  5. <tx:attributes>
  6. <tx:method name="createOrder" propagation="REQUIRED" />
  7. <tx:method name="updateOrderStatus" propagation="REQUIRED" />
  8. tx:attributes>
  9. tx:advice>
  10. <aop:config>
  11. <aop:pointcut id="txPointcut" expression="execution(* com.example.order.service..*.*(..))" />
  12. <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
  13. aop:config>

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

  1. @Service
  2. public class OrderService {
  3. @Autowired
  4. private OrderDao orderDao;
  5. @Transactional
  6. public void createOrder(Order order) {
  7. // 创建订单的业务逻辑
  8. orderDao.createOrder(order);
  9. }
  10. @Transactional
  11. public void updateOrderStatus(Long orderId, String status) {
  12. // 更新订单状态的业务逻辑
  13. orderDao.updateOrderStatus(orderId, status);
  14. }
  15. }

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

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

二、Spring事务配置介绍

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

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

(一)Spring事务属性介绍

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

枚举值变量名说明
0PROPAGATION_REQUIRED当前方法必须运行在事务中。如果当前有事务,则方法将会在该事务中运行。否则就启动一个新的事务
1PROPAGATION_SUPPORTS当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
2PROPAGATION_MANDATORY该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
3PROPAGATION_REQUIRES_NEW当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果当前有事务则会被挂起
4PROPAGATION_NOT_SUPPORTED该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。
5PROPAGATION_NEVER当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常,这和2正好相反
6PROPAGATION_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">
  3. <property name="dataSource" ref="dataSource" />
  4. bean>
  5. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  6. <tx:attributes>
  7. <tx:method name="*" propagation="REQUIRED" />
  8. tx:attributes>
  9. tx:advice>
  10. <aop:config>
  11. <aop:pointcut id="pointsCut" expression="execution(* org.zyf.keep.crm.service.*Service.update*(..))" />
  12. <aop:advisor advice-ref="txAdvice" pointcut-ref="pointsCut" />
  13. aop:config>

基于注解的配置

主要配置如下:

  1. <bean id="dataSource" class="xxx.DataSource" />
  2. <bean name="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  3. <property name="dataSource" ref="dataSource" />
  4. bean>
  5. <tx:annotation-driven transaction-manager="transactionManager"/>

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

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Transactional
  4. public void update(UserDO user){
  5. //....
  6. }
  7. }

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

  1. public @interface Transactional {
  2. @AliasFor("transactionManager")
  3. String value() default ""; // transactionManager 的别名
  4. @AliasFor("value")
  5. String transactionManager() default ""; //value的别名
  6. Propagation propagation() default Propagation.REQUIRED; //传播行为
  7. Isolation isolation() default Isolation.DEFAULT; //隔离级别,数据库默认
  8. int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;//超时时间,数据库默认
  9. boolean readOnly() default false; //是否为只读事务,默认非只读
  10. Classextends Throwable>[] rollbackFor() default {}; //需要回滚的异常类
  11. String[] rollbackForClassName() default {}; //需要回滚的异常类名
  12. Classextends Throwable>[] noRollbackFor() default {};//不回滚的异常类
  13. String[] noRollbackForClassName() default {};//不回滚的异常类名
  14. }
  15. ​

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

三、分析Spring事务实现原理

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

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

(一)整体流程梳理

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

 (二)核心实现分析

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

annotation-driven 解析

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

  1. /*
  2. * Copyright 2002-2012 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.transaction.config;
  17. import org.w3c.dom.Element;
  18. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
  19. /**
  20. * {@code NamespaceHandler} allowing for the configuration of
  21. * declarative transaction management using either XML or using annotations.
  22. *
  23. *

    This namespace handler is the central piece of functionality in the

  24. * Spring transaction management facilities and offers two approaches
  25. * to declaratively manage transactions.
  26. *
  27. *

    One approach uses transaction semantics defined in XML using the

  28. * {@code } elements, the other uses annotations
  29. * in combination with the {@code } element.
  30. * Both approached are detailed to great extent in the Spring reference manual.
  31. *
  32. * @author Rob Harrop
  33. * @author Juergen Hoeller
  34. * @since 2.0
  35. */
  36. public class TxNamespaceHandler extends NamespaceHandlerSupport {
  37. static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
  38. static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
  39. static String getTransactionManagerName(Element element) {
  40. return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
  41. element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
  42. }
  43. @Override
  44. public void init() {
  45. registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
  46. registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
  47. registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
  48. }
  49. }

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

  1. /*
  2. * Copyright 2002-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.transaction.config;
  17. import org.w3c.dom.Element;
  18. import org.springframework.aop.config.AopNamespaceUtils;
  19. import org.springframework.beans.factory.config.BeanDefinition;
  20. import org.springframework.beans.factory.config.RuntimeBeanReference;
  21. import org.springframework.beans.factory.parsing.BeanComponentDefinition;
  22. import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
  23. import org.springframework.beans.factory.support.RootBeanDefinition;
  24. import org.springframework.beans.factory.xml.BeanDefinitionParser;
  25. import org.springframework.beans.factory.xml.ParserContext;
  26. import org.springframework.transaction.event.TransactionalEventListenerFactory;
  27. import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
  28. import org.springframework.transaction.interceptor.TransactionInterceptor;
  29. /**
  30. * {@link org.springframework.beans.factory.xml.BeanDefinitionParser
  31. * BeanDefinitionParser} implementation that allows users to easily configure
  32. * all the infrastructure beans required to enable annotation-driven transaction
  33. * demarcation.
  34. *
  35. *

    By default, all proxies are created as JDK proxies. This may cause some

  36. * problems if you are injecting objects as concrete classes rather than
  37. * interfaces. To overcome this restriction you can set the
  38. * '{@code proxy-target-class}' attribute to '{@code true}', which
  39. * will result in class-based proxies being created.
  40. *
  41. * @author Juergen Hoeller
  42. * @author Rob Harrop
  43. * @author Chris Beams
  44. * @author Stephane Nicoll
  45. * @since 2.0
  46. */
  47. class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
  48. /**
  49. * Parses the {@code } tag. Will
  50. * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
  51. * with the container as necessary.
  52. */
  53. @Override
  54. public BeanDefinition parse(Element element, ParserContext parserContext) {
  55. registerTransactionalEventListenerFactory(parserContext);
  56. String mode = element.getAttribute("mode");
  57. if ("aspectj".equals(mode)) {
  58. // mode="aspectj"
  59. registerTransactionAspect(element, parserContext);
  60. }
  61. else {
  62. // mode="proxy"
  63. AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
  64. }
  65. return null;
  66. }
  67. private void registerTransactionAspect(Element element, ParserContext parserContext) {
  68. String txAspectBeanName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME;
  69. String txAspectClassName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_CLASS_NAME;
  70. if (!parserContext.getRegistry().containsBeanDefinition(txAspectBeanName)) {
  71. RootBeanDefinition def = new RootBeanDefinition();
  72. def.setBeanClassName(txAspectClassName);
  73. def.setFactoryMethodName("aspectOf");
  74. registerTransactionManager(element, def);
  75. parserContext.registerBeanComponent(new BeanComponentDefinition(def, txAspectBeanName));
  76. }
  77. }
  78. private static void registerTransactionManager(Element element, BeanDefinition def) {
  79. def.getPropertyValues().add("transactionManagerBeanName",
  80. TxNamespaceHandler.getTransactionManagerName(element));
  81. }
  82. private void registerTransactionalEventListenerFactory(ParserContext parserContext) {
  83. RootBeanDefinition def = new RootBeanDefinition();
  84. def.setBeanClass(TransactionalEventListenerFactory.class);
  85. parserContext.registerBeanComponent(new BeanComponentDefinition(def,
  86. TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME));
  87. }
  88. /**
  89. * Inner class to just introduce an AOP framework dependency when actually in proxy mode.
  90. */
  91. private static class AopAutoProxyConfigurer {
  92. public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
  93. //1. 在必要的时候注册 InfrastructureAdvisorAutoProxyCreator,默认情况下,会注册的
  94. AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
  95. String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
  96. if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
  97. Object eleSource = parserContext.extractSource(element);
  98. // Create the TransactionAttributeSource definition.
  99. RootBeanDefinition sourceDef = new RootBeanDefinition(
  100. "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
  101. sourceDef.setSource(eleSource);
  102. sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  103. String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
  104. // Create the TransactionInterceptor definition.
  105. RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
  106. interceptorDef.setSource(eleSource);
  107. interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  108. registerTransactionManager(element, interceptorDef);
  109. interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
  110. String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
  111. // Create the TransactionAttributeSourceAdvisor definition.
  112. RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
  113. advisorDef.setSource(eleSource);
  114. advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  115. advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
  116. advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
  117. if (element.hasAttribute("order")) {
  118. advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
  119. }
  120. parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
  121. CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
  122. compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
  123. compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
  124. compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
  125. parserContext.registerComponent(compositeDef);
  126. }
  127. }
  128. }
  129. }

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

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

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 完成最后的处理

相关代码如下:

  1. @Override
  2. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
  3. AnnotationAttributes attributes =
  4. AnnotatedElementUtils.getMergedAnnotationAttributes(ae,
  5. Transactional.classTransactional.class);
  6. if (attributes != null) {
  7. return parseTransactionAnnotation(attributes);
  8. }
  9. }
  10. //处理注解上的一些属性
  11. protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
  12. RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
  13. Propagation propagation = attributes.getEnum("propagation");
  14. rbta.setPropagationBehavior(propagation.value());
  15. // 省略一部分
  16. rbta.getRollbackRules().addAll(rollBackRules);
  17. return rbta;
  18. }

从代码实现上来看,判断标准就是看是否有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方法,其主要逻辑如下:

  1. protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation)
  2. throws Throwable {
  3. ​
  4. // 1. 获取并解析当时设置的事务配置属性,这个属性由方法上的注解来设置
  5. final TransactionAttribute txAttr =
  6. getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
  7. //2. 获取平台相关的事务管理器,这个需要手动在xml文件里配置
  8. final PlatformTransactionManager tm = determineTransactionManager(txAttr);
  9. //3. 获取连接点
  10. final String joinpointIdentification = methodIdentification(method, targetClass);
  11. ​
  12. if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
  13. // 一般来说,平台的事务管理器都不会继承自CallbackPreferringPlatformTransactionManager,所以这个分支会走到。
  14. //4. 创建事务对象,开启事务等一系列操作
  15. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
  16. Object retVal = null;
  17. try {
  18. // 执行业务代码
  19. retVal = invocation.proceedWithInvocation();
  20. } catch (Throwable ex) {
  21. // 发生异常,完成事务,可能是回滚也可能是提交
  22. completeTransactionAfterThrowing(txInfo, ex);
  23. throw ex;
  24. } finally {
  25. //清理事务
  26. cleanupTransactionInfo(txInfo);
  27. }
  28. //提交事务
  29. commitTransactionAfterReturning(txInfo);
  30. return retVal;
  31. }
  32. ​

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

整体总结如下:

image.png

 四、总结

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

参考文献

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

文章知识点与官方知识档案匹配,可进一步学习相关知识
Java技能树首页概览148946 人正在系统学习中
注:本文转载自blog.csdn.net的张彦峰ZYF的文章"https://zyfcodes.blog.csdn.net/article/details/130061447"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top