目录
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: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标签的配置,核心配置示例如下:
- <bean id="dataSource" class="xxx.DataSource" />
- <bean name="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="*" propagation="REQUIRED" />
- tx:attributes>
- tx:advice>
- <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>
基于注解的配置
主要配置如下:
- <bean id="dataSource" class="xxx.DataSource" />
- <bean name="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- bean>
- <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机制来创建为目标类应用上事务切面。基于注解的关键配置有两个,一个是
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.
- *
- *
This namespace handler is the central piece of functionality in the
- * Spring transaction management facilities and offers two approaches
- * to declaratively manage transactions.
- *
- *
One approach uses transaction semantics defined in XML using the
- * {@code
} elements, the other uses annotations - * in combination with the {@code
} 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.
- *
- *
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
} 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和动态代理,在异步和缓存场景下都会用到,包括我们自己扩展一些能力出来的时候也会用到。
评论记录:
回复评论: