目录
一、Spring事务管理介绍
(一)基本理论
(二)实际工作中的举例
(三)简单应用举例
二、Spring事务配置介绍
(一)Spring事务属性介绍
传播属性(传播行为)可选值说明
(二)声明式事务配置示例
基于XML的配置
基于注解的配置
三、分析Spring事务实现原理
(一)整体流程梳理
(二)核心实现分析
annotation-driven 解析
代理类创建分析
寻找合适通知器的时序图展示
InfrastructureAdvisorAutoProxyCreator处理器注册与实例化展示
为使用事务的目标类生成代理的时序图展示
事务处理分析
四、总结
参考文献
一、Spring事务管理介绍
(一)基本理论
在软件开发中,事务管理是一种处理数据库操作的方式,它可以确保一组数据库操作要么全部成功执行,要么全部失败回滚,从而保持数据的一致性和完整性。
Spring框架是一个流行的Java企业级应用程序开发框架,提供了强大的事务管理功能,可以轻松地在Spring应用程序中管理数据库事务。Spring事务管理通过提供一种声明性事务管理的方式,将事务管理从业务逻辑中解耦,并通过使用AOP(面向切面编程)技术实现事务管理。Spring框架支持多种事务管理策略,包括本地事务和分布式事务。
Spring事务管理的一些核心概念和功能包括:
- 事务管理器(Transaction Manager):负责管理数据库事务的对象。Spring支持多种事务管理器,如JDBC事务管理器、Hibernate事务管理器、JTA事务管理器等。
- 事务传播行为(Transaction Propagation):定义了在多个事务方法相互调用时,事务如何传播的规则。例如,一个事务方法A调用了另一个事务方法B,事务传播行为定义了B方法是加入A方法的事务,还是创建一个新的事务。
- 事务隔离级别(Transaction Isolation Level):定义了事务对数据库的隔离程度。不同的隔离级别提供了不同的并发控制策略,例如读未提交、读已提交、可重复读和串行化。
- 事务回滚(Transaction Rollback):当事务操作失败时,事务管理器可以自动回滚所有已执行的数据库操作,从而将数据库状态恢复到事务开始前的状态。
- 声明式事务管理(Declarative Transaction Management):通过在方法或类级别上使用Spring的事务管理注解,如@Transactional,可以将事务管理规则与业务逻辑解耦。这样,开发人员可以在代码中专注于业务逻辑,而不需要显式地编写事务管理代码。
- 编程式事务管理(Programmatic Transaction Management):可以使用编程方式通过编写事务管理代码来管理事务,例如使用Spring的TransactionTemplate API。
Spring事务管理可以应用于多种数据访问技术,如JDBC、Hibernate、JPA等,并且可以与其他Spring特性,如Spring的AOP和Spring的IoC容器一起使用,从而提供了一个强大的事务管理解决方案,使开发人员能够轻松地实现事务管理,并确保数据的一致性和完整性。
(二)实际工作中的举例
在实际工作中,Spring事务管理广泛应用于各种Java企业级应用程序中,包括Web应用、后端服务、批处理作业等。以下是一些使用Spring事务管理的实际应用举例:
- 电商应用:在一个电商应用中,订单管理涉及到对多个数据库表的增、删、改等操作,例如创建订单、更新库存、扣减账户余额等。使用Spring事务管理,可以确保这些数据库操作要么全部成功提交,要么全部回滚,从而保持订单和库存、账户之间的一致性。
- 银行系统:在一个银行系统中,涉及到对账户的转账、存款、取款等操作,这些操作必须保证在一个事务中进行,以确保资金的正确处理。使用Spring事务管理,可以将这些操作封装在一个事务中,从而保持账户操作的一致性和完整性。
- 社交媒体应用:在一个社交媒体应用中,用户可能同时进行多个操作,例如发布帖子、评论、点赞等。使用Spring事务管理,可以将这些操作组织成一个事务,从而保持数据的一致性,例如在发布帖子时,同时插入帖子信息和更新用户的动态信息。
- 后端服务:在后端服务中,可能会涉及到多个数据库操作,例如从多个数据源中读取数据、更新多个数据库表等。使用Spring事务管理,可以将这些操作组织成一个事务,从而保证数据操作的一致性。
- 批处理作业:在批处理作业中,可能需要对大量数据进行处理,包括从文件读取数据、处理数据、将结果写入数据库等。使用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 {
@Autowired
private OrderDao orderDao;
@Transactional
public void createOrder(Order order) {
// 创建订单的业务逻辑
orderDao.createOrder(order);
}
@Transactional
public 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 {
@Transactional
public 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);
}
@Override
public 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.
*/
@Override
public 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 接口,同时其两个属性transactionAttributeSource和adviceBeanName 分别引用了AnnotationTransactionAttributeSource和TransactionInterceptor
annotation-driven 解析简单小结
- tx 是一个自定义标签,由一个对应的命名处理器 TxNamespaceHandler 来实现
- TxNamespaceHandler 在初始化是地,为 annotation-driven 注册了一个对应的解析器 AnnotationDrivenBeanDefinitionParser
- 该解析器在解析的时候,注册了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 注解。
寻找合适通知器的时序图展示
InfrastructureAdvisorAutoProxyCreator处理器注册与实例化展示
为使用事务的目标类生成代理的时序图展示
至此,我们就分析完了spring事务切面查询与匹配校验,最终得到的切面列表,如果不为空的话,则会作为创建代理类的参数之一,由AbstractAutoProxyCreator 来完成 最终代理类的创建,由于这个是spring aop本身的逻辑,已经与事务无关,所以就不再继续分析了。总结如下:
- 创建代理类的关键在于是否能查找到匹配的切面
- spring 在解析 annotation-driven 时,就自动注册了4个和事务相关的类的实体,其中包括切面查找类和切面类的实例
- 查找切面之后,还需要对切面进行过滤,以判断是否能够关联切面,该过滤方法经层层委托,最后由一个注解处理器来实现,其实现标准就是看类或方法上是否有Transactional 注解
- 通过上述流程,可以保证给且只给带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管理自身的一些元素,所以相对要复杂一些,这里也进行简单的介绍。
整体总结如下:
四、总结
Spring事务能力的支撑用到了很多知识,动态代理、AOP、反射、后置处理器等等,总的来说就是应用启动时为需要使用事务的类生成代理类,以及将事务能力(拦截逻辑)织入进去,在实例化的时候调用后置处理器的逻辑,将代理类实例化替代目标类,并放入上下文容器中,在实际调用目标类事务方法的时候,被代理类中ReflectiveMethodInvocation拦截,然后先调用拦截器中的事务逻辑,然后再调用目标类的业务逻辑,最后处理异常回滚和提交,看起来比较简单,但是框架层面提供了非常庞大的基础组件来支撑和实现事务能力,当然这些基础组件大部分都会复用,比如AOP和动态代理,在异步和缓存场景下都会用到,包括我们自己扩展一些能力出来的时候也会用到。
参考文献
1.Spring事务原理详解_叔牙的博客-CSDN博客