Spring Dao编程原理-Spring事务的生效原理

从 0 开始深入学习 Spring 专栏目录总览

好了,到了 Dao 的原理部分,意味着接下来的几章又是挑战了。不过相比起 IOC 和 AOP 的内容来看,事务的原理相对难度更低,接受度更高。

咱先从事务的生效原理说起,对于声明式事务来讲,基于 xml 和基于注解驱动的激活方式有些不太一样,咱分别来研究。

1. 基于xml的声明式事务生效原理

基于 xml 的事务只需要声明一个 <tx:advice> 标签,并配置好对应的内容,以及切入点表达式,就算是完事了。那这个 <tx:advice> 标签的背后一定是一个事务激活的机制了。要想研究明白它,首先要看看这个标签底层对应的是什么。

1.1 tx:advice对应的底层

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="saveAndQuery"/>
        <tx:method name="addMoney"/>
        <tx:method name="subtractMoney"/>
    </tx:attributes>
</tx:advice>

在这个 xml 的配置中,配置事务通知,需要指定事务管理器,并配置事务覆盖的方法,必要的还会配置事务传播行为。而这个 <tx:advice> 标签的底层,其实是注册了一个 TransactionInterceptor ,究其底层,咱可以在 IDEA 中直接点开这个标签,便可以发现这样一段代码:

<xsd:element name="advice">
    <xsd:complexType>
        <xsd:annotation>
            <xsd:documentation source="java:org.springframework.transaction.interceptor.TransactionInterceptor">
                ......

它用一个文档的形式引用了 TransactionInterceptor 这个类,包括在下面它还反复多次的出现了,那咱就跳转到这个类中看一看。

跳转过来之后,借助 IDEA 会发现,在这个类的左边竟然有一个符号标识,咱经过前面的 IOC 部分早就知道,这个符号是代表有配置文件中注册过这个类,换句话说,这个类会在某个 xml 配置文件的驱动下创建对应的 bean 。

Spring Dao编程原理-Spring事务的生效原理

鼠标点击,会跳转到 <tx:advice> 标签上,说明 <tx:advice> 标签的底层真的就是注册了一个 TransactionInterceptor 。

1.2 TransactionInterceptor的结构

既然基于 xml 配置文件的声明式事务控制,核心就是这样一个 TransactionInterceptor ,那它的结构自然要成为我们研究的重点。

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable

先看一眼继承,可以发现它实现了 MethodInterceptor 接口!通过前面 AOP 部分的学习,我们知道 MethodInterceptor 接口是 AOP 增强的核心拦截器接口,利用 AOP 生成的代理对象中都会包含一组 MethodInterceptor 接口的实现类对象。

除了观察继承和实现,更主要的还是看看文档注释吧,这个类的文档注释写的还蛮好的:

AOP Alliance MethodInterceptor for declarative transaction management using the common Spring transaction infrastructure (PlatformTransactionManager/ org.springframework.transaction.ReactiveTransactionManager). Derives from the TransactionAspectSupport class which contains the integration with Spring’s underlying transaction API. TransactionInterceptor simply calls the relevant superclass methods such as invokeWithinTransaction in the correct order. TransactionInterceptors are thread-safe.

它是 AOP 联盟定义的一个 MethodInterceptor ,用于使用常见的 Spring 事务基础结构(PlatformTransactionManager / ReactiveTransactionManager)进行声明式事务管理。 它从 TransactionAspectSupport 类派生,该类包含与 Spring 的基础事务 API 的集成。 TransactionInterceptor 只是以正确的顺序调用相关的超类方法,例如 invokeWithinTransaction 方法。 TransactionInterceptor 是线程安全的。

文档注释中有提到一点,TransactionInterceptor 还有一个父类 TransactionAspectSupport ,这个类中有一些与 SpringFramework 的基础事务 API 的集成(例如执行事务的核心方法 invokeWithinTransaction 、创建事务、提交事务、回滚事务等),这些方法的具体使用,咱放到下一章的事务执行流程中再学习研究。

除此之外,基于 xml 的声明式事务用到的核心 API 就没了,,,没了,,,所以还是非常简单的吧!下面咱继续研究基于注解的声明式事务,它里面都有哪些核心的组件。

2. 基于注解的声明式事务生效原理

基于注解的声明式事务,需要在配置类上标注 @EnableTransactionManagement 注解,之后注册一个事务管理器就 OK 了。同样的,咱还是从这个模块装配的注解开始研究。

2.1 @EnableTransactionManagement

不用打开源码,我们就已经对这个注解的套路了如指掌了,它铁定又 @Import 组件了!当然,源码也确实是这么设计的:

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	boolean proxyTargetClass() default false;
	AdviceMode mode() default AdviceMode.PROXY;
	int order() default Ordered.LOWEST_PRECEDENCE;
}

这个导入的 TransactionManagementConfigurationSelector 咱暂且放在一边,咱先看看这里面的几个属性。

2.1.1 proxyTargetClass

这个属性简单的很,是否直接代理目标类。一看就知道这是关联的 AOP 的东西,AOP 在不主动声明的情况下,是有接口就用 jdk 动态代理,没有接口就用 Cglib 动态代理。如果 proxyTargetClass 属性声明为 true ,则所有被事务增强的类全部使用 Cglib 代理类的方式。

2.1.2 mode

这个属性的取值有两个,分别是 PROXY 和 ASPECTJ ,乍一看我们会认为这两个的底层实现是差不多的,都是代理目标类的对象,在运行期对原有方法添加事务控制的逻辑。然而实际并不是这样,PROXY 代表的确实是运行期增强,但 ASPECTJ 代表的是类加载期增强(类似于 load-time-weaving )。哎这样一说是不是立马就明白了?跟 AOP 一样,我们一般都是使用运行期的动态代理来实现 AOP 或者事务控制,但也可以手动调整通知织入的时机为类加载期。

但是话又说回来了,如果用运行期的动态代理好好地,为啥会再留出一个类加载期的事务控制织入呢?只是为了跟 AOP 迎合吗?这倒不是,设置类加载期的事务控制是为了在 Service 的方法中调用自身类的其他方法考虑的,咱之前学习 AOP 的时候知道,如果代理对象要调用自身类的其他方法,只是用 this 调用的话是不会再次触发通知逻辑执行的,而借助 AopContext 拿到当前代理对象后,再调用自身类的其他方法,就可以再次触发通知逻辑。SpringFramework 的事务增强也是如此,如果使用默认的运行期动态代理织入事务控制的通知,则 Service 中调用其他受事务控制的方法时,不会再次触发事务控制的逻辑,如果使用 ASPECTJ 在类加载器织入事务控制的通知,则该问题可以解决。

不过话说回来,如果要把 mode 改为 ASPECTJ 模式的话,需要额外导入一个依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.framework.version}</version>
</dependency>

除此之外,还需要开启 load-time-weaving ,这个我们实在是不常用,小伙伴们知道一下就好了。

2.1.3 order

这个属性又是很好理解了吧,它指定的是事务通知的执行顺序。前面在 AOP 中我们知道,通知是可以指定执行顺序的,通过 @Order 注解或者 Ordered 接口就可以搞定。此处 @EnableTransactionManagement 帮我们暴露了这个属性的设置,可以说是相当方便了。

2.2 TransactionManagementConfigurationSelector

@EnableTransactionManagement 注解的属性看完了,接下来是它导入的核心选择器了,这个类一看就知道是 ImportSelector ,咱进入源码中来看:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}
}

可见它会根据 @EnableTransactionManagement 注解的 mode 属性,决定导入哪些组件:

  • PROXY – AutoProxyRegistrar + ProxyTransactionManagementConfiguration
  • ASPECTJ – TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME

下面的 TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME 和 JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME 在 spring-tx 包中是找不到的,它们都在 spring-aspects 包下,还是那句话,太不常用,所以小册不深入研究这部分,感兴趣的小伙伴可以自行研究。

咱重点还是看 PROXY 下导入的这两个组件。

2.3 AutoProxyRegistrar

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar

这个家伙又是实现了 ImportBeanDefinitionRegistrar 的,那它的作用肯定是向 BeanDefinitionRegistry 中继续注册新的 BeanDefinition 咯,咱进入核心方法中看:(重要部分已标有注释)

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    for (String annType : annTypes) {
        // 搜寻所有标注的注解
        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (candidate == null) {
            continue;
        }
        // 获取注解上的mode和proxyTargetClass属性
        Object mode = candidate.get("mode");
        Object proxyTargetClass = candidate.get("proxyTargetClass");
        if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
                Boolean.class == proxyTargetClass.getClass()) {
            candidateFound = true;
            // 当mode为PROXY时,会注册额外的BeanDefinition
            if (mode == AdviceMode.PROXY) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                if ((Boolean) proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                    return;
                }
            }
        }
    }
    // logger ......
}

注意看中下部分的那个 if 判断,如果 mode 为 PROXY ,则会向 BeanDefinitionRegistry 中注册额外的 BeanDefinition ,这些 BeanDefinition 想都不用想,肯定是注册与事务控制相关的 AOP 组件了(看工具类的类名也能推测出来嘛)。

咱继续往里深入,看看这个 AopConfigUtils 注册了个啥子东西:

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAutoProxyCreatorIfNecessary(registry, null);
}

public static BeanDefinition registerAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    // 注意类型在这里!
    return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

看下面,它注册了一个类型为 InfrastructureAdvisorAutoProxyCreator 的 bean !这个家伙是干嘛的呢?

2.3.1 InfrastructureAdvisorAutoProxyCreator

翻开它的源码,文档注释似乎已经告诉了我们全部:

Auto-proxy creator that considers infrastructure Advisor beans only, ignoring any application-defined Advisors.

自动代理创建器,仅考虑基础结构 Advisor 类型的 Bean,而忽略任何程序定义的 Advisor 。

从这段简短的 javadoc 中,我们可以得知以下信息:

  • 它是 AOP 的代理创建器
  • 它只会组合基础结构类型的 Advisor ,我们自己写的它一概不理

本来这些信息我们都还算能整明白,但唯独一点:基础结构,这是个啥???这样,小册先上几行代码:

int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;

是不是突然产生了一点点印象?这不就是 BeanDefinition 中的那个 role 属性吗?(org.springframework.beans.factory.support.AbstractBeanDefinition#role)那合着这里面的基础结构,实际上就是指的 ROLE_INFRASTRUCTURE 这个 BeanDefinition 的角色呀。被标注为 ROLE_INFRASTRUCTURE 的 BeanDefinition ,它们通常都是 SpringFramework 内部的 bean ,且在应用程序中起到基础支撑性的作用。而 InfrastructureAdvisorAutoProxyCreator 就是关心这种类型的 BeanDefinition ,那我们在这里就可以先大胆猜测一波:TransactionInterceptor 作为一个 MethodInterceptor ,它被包装为 Advisor 后,role 肯定也是 ROLE_INFRASTRUCTURE 。是不是真的这样呢,咱要继续往下看了。

2.4 ProxyTransactionManagementConfiguration

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration

这个类的头就开始搞事情:这个 ProxyTransactionManagementConfiguration 本身也被标注了 ROLE_INFRASTRUCTURE 的角色,由此也能进一步理解 BeanDefinition 的三种角色了吧。( 有个小细节,SpringFramework 5.2 之前并没有标注这个东东,原因有待考究)

除此之外,还要注意一点:@Configuration(proxyBeanMethods = false) ,它是什么意思呢?这个细节我们在之前的 IOC 章节中没有提到,这里简单说一下:SpringFramework 在解析注解配置类时,会给每个标注了 @Bean 注解的方法都包装一层代理,使这些方法的内部逻辑只能调用一次,第一次逻辑执行完毕后的方法返回值会缓存在代理中,以后再调用这些 @Bean 方法时,会直接返回代理中缓存的返回值。如果在 @Configuration 注解上声明 proxyBeanMethods 属性为 false ,则不会给这些 @Bean 方法构造缓存代理,每次执行方法都会走里面的逻辑了。有关这里面的细节和原理,小册会考虑在正文更新完毕后,在最后追加一些加餐章节讲解。

接下来是这个类注册的组件了,这个类的本体就注册了 3 个,咱逐个来看。

2.4.1 transactionAttributeSource:事务配置源

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
}

没有任何多余的逻辑,只是创建了一个 AnnotationTransactionAttributeSource 就返回了。注意它的类型是 TransactionAttributeSource ,我们先来讲解它。

2.4.1.1 TransactionAttributeSource

这个接口在 SpringFramework 5.2 之前只有一个方法,随着 5.2 之后引入了响应式事务控制,才多了另外一个方法:

public interface TransactionAttributeSource {
	default boolean isCandidateClass(Class<?> targetClass) {
		return true;
	}

	@Nullable
	TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}

下面的 getTransactionAttribute 方法才是重中之重,它可以根据一个类 + 方法,解析转换为 TransactionAttribute ,而 TransactionAttribute 咱前面在第 60 章事务控制模型中已经讲过,它本身是 TransactionDefinition ,所以我们可以这样理解:TransactionAttributeSource 可以根据一个具体的类中的方法,解析转换为一个 TransactionDefinition ( TransactionAttribute )

借助 IDE ,可以发现它的几个实现类,其中就包含了上面它创建的 AnnotationTransactionAttributeSource ,除此之外还有一些其他的实现,小伙伴们可以自行翻看,小册这里不详细展开,咱主要关注的是它创建的这个 AnnotationTransactionAttributeSource 。

Spring Dao编程原理-Spring事务的生效原理

2.4.1.2 AnnotationTransactionAttributeSource

先看一眼 javadoc 吧:

Implementation of the org.springframework.transaction.interceptor.TransactionAttributeSource interface for working with transaction metadata in JDK 1.5+ annotation format. This class reads Spring’s JDK 1.5+ Transactional annotation and exposes corresponding transaction attributes to Spring’s transaction infrastructure. Also supports JTA 1.2’s javax.transaction.Transactional and EJB3’s javax.ejb.TransactionAttribute annotation (if present). This class may also serve as base class for a custom TransactionAttributeSource, or get customized through TransactionAnnotationParser strategies.

它是 TransactionAttributeSource 接口的实现,用于处理 JDK 1.5+ 事务注解的事务元数据。

这个类会读取 Spring 的 @Transactional 注解,并将相应的 TransactionAttribute 事务属性公开给 Spring 的事务底层件。此外,还支持 JTA 1.2 的 javax.transaction.Transactional 和 EJB3 的 javax.ejb.TransactionAttribute 注解(如果存在的话)。此类也可用作自定义 TransactionAttributeSource 的父类,或通过 TransactionAnnotationParser 的策略进行自定义。

说了这么多,我们只关心一句话:它读取 @Transactional 注解,并封装 TransactionAttribute 。由此可见 AnnotationTransactionAttributeSource 是读取和解析标注有 @Transactional 注解的方法的。

至于里面是怎么解析的,用的什么方式解析,咱同样放到下一章再来研究。

2.4.2 transactionInterceptor:事务拦截器

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
    TransactionInterceptor interceptor = new TransactionInterceptor();
    interceptor.setTransactionAttributeSource(transactionAttributeSource);
    if (this.txManager != null) {
        interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
}

果然,注解声明式事务也是离不开这个 TransactionInterceptor 的,这里同样会以编程式的方式创建。

注意这里它依赖了 TransactionAttributeSource ,而且依赖的方式是使用了方法参数,而不是直接在方法内部调用 transactionAttributeSource() 方法,所以也就不需要给这个配置类代理了。

2.4.3 transactionAdvisor:事务增强器

@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
        TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    advisor.setTransactionAttributeSource(transactionAttributeSource);
    advisor.setAdvice(transactionInterceptor);
    // 取@EnableTransactionManagement的order属性
    if (this.enableTx != null) {
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
}

Advisor 增强器!终于发现增强器本体了,这里它使用的是 BeanFactoryTransactionAttributeSourceAdvisor 作为增强器的落地实现,并组合了 TransactionInterceptor 事务拦截器和 TransactionAttributeSource 事务配置源。咱先看看这个增强器本身的东西,老套路,从 javadoc 开始吧:

Advisor driven by a TransactionAttributeSource, used to include a transaction advice bean for methods that are transactional.

它是由 TransactionAttributeSource 驱动的增强器,用于为开启事务的 bean 的方法增强事务通知。

TransactionAttributeSource 驱动?上面我们只看到了 TransactionInterceptor ,没有看到 Pointcut 的身影,那它是怎么搞定切入点的呢(咱都学过了,猜都能猜到肯定是判断类或者方法上有没有打 @Transactional 注解)?咱还是进到 BeanFactoryTransactionAttributeSourceAdvisor 中看看结构吧。

点进源码,一上来就看到了答案的曙光:

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

	@Nullable
	private TransactionAttributeSource transactionAttributeSource;

	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return transactionAttributeSource;
		}
	};
    // ......

诶?原来是有切入点的啊,只不过这个切入点的类型是以 TransactionAttributeSource 为依据的。。。那看来判断一个 bean 是否可以被事务通知增强,需要拿 TransactionAttributeSource 判断才是啊。哎好巧不巧,TransactionAttributeSource 刚好能根据一个类的一个方法,获取到对应的 TransactionAttribute !貌似这一切都讲的过去了!那事实是不是真的像我们推测的那样呢?

当然没错,翻开 TransactionAttributeSourcePointcut 的核心 matches 方法,会发现它就是拿 TransactionAttributeSource 去根据方法和方法所属类,判断是否有对应的事务定义信息(是否被 @Transactional 注解标注),与咱的推测完全一致。

@Override
public boolean matches(Method method, Class<?> targetClass) {
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

到这里,有关 ProxyTransactionManagementConfiguration 中注册的三个组件就都搞明白了,注意 ProxyTransactionManagementConfiguration 还有个父类,咱继续往上爬。

2.5 AbstractTransactionManagementConfiguration

这个类中还有一个组件的注册:

@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
    return new TransactionalEventListenerFactory();
}

很明显这是事务监听器的工厂,它就是处理前面学到的事务事件监听器的。这里面有两个核心的方法,小伙伴们一看就明白:

@Override
public boolean supportsMethod(Method method) {
    // 支持被@TransactionalEventListener标注的事件监听方法
    return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);
}

@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
    // 根据事件监听的方法,创建实际的事件监听器
    return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);
}

除此之外的内容都不算很重要了,咱就不管了。

【到此为止,咱就把 SpringFramework 中声明式事务的生效原理和底层都扒出来了,可以发现核心还是蛮清晰的:一个 TransactionInterceptor ,以及配合的增强器等组件。下一章,咱以前面做过的例子为参考,来实际的研究声明式事务是如何工作、起作用的】

好了,到了 Dao 的原理部分,意味着接下来的几章又是挑战了。不过相比起 IOC 和 AOP 的内容来看,事务的原理相对难度更低,接受度更高。

咱先从事务的生效原理说起,对于声明式事务来讲,基于 xml 和基于注解驱动的激活方式有些不太一样,咱分别来研究。

1. 基于xml的声明式事务生效原理

基于 xml 的事务只需要声明一个 <tx:advice> 标签,并配置好对应的内容,以及切入点表达式,就算是完事了。那这个 <tx:advice> 标签的背后一定是一个事务激活的机制了。要想研究明白它,首先要看看这个标签底层对应的是什么。

1.1 tx:advice对应的底层

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="saveAndQuery"/>
        <tx:method name="addMoney"/>
        <tx:method name="subtractMoney"/>
    </tx:attributes>
</tx:advice>

在这个 xml 的配置中,配置事务通知,需要指定事务管理器,并配置事务覆盖的方法,必要的还会配置事务传播行为。而这个 <tx:advice> 标签的底层,其实是注册了一个 TransactionInterceptor ,究其底层,咱可以在 IDEA 中直接点开这个标签,便可以发现这样一段代码:

<xsd:element name="advice">
    <xsd:complexType>
        <xsd:annotation>
            <xsd:documentation source="java:org.springframework.transaction.interceptor.TransactionInterceptor">
                ......

它用一个文档的形式引用了 TransactionInterceptor 这个类,包括在下面它还反复多次的出现了,那咱就跳转到这个类中看一看。

跳转过来之后,借助 IDEA 会发现,在这个类的左边竟然有一个符号标识,咱经过前面的 IOC 部分早就知道,这个符号是代表有配置文件中注册过这个类,换句话说,这个类会在某个 xml 配置文件的驱动下创建对应的 bean 。

Spring Dao编程原理-Spring事务的生效原理

鼠标点击,会跳转到 <tx:advice> 标签上,说明 <tx:advice> 标签的底层真的就是注册了一个 TransactionInterceptor 。

1.2 TransactionInterceptor的结构

既然基于 xml 配置文件的声明式事务控制,核心就是这样一个 TransactionInterceptor ,那它的结构自然要成为我们研究的重点。

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable

先看一眼继承,可以发现它实现了 MethodInterceptor 接口!通过前面 AOP 部分的学习,我们知道 MethodInterceptor 接口是 AOP 增强的核心拦截器接口,利用 AOP 生成的代理对象中都会包含一组 MethodInterceptor 接口的实现类对象。

除了观察继承和实现,更主要的还是看看文档注释吧,这个类的文档注释写的还蛮好的:

AOP Alliance MethodInterceptor for declarative transaction management using the common Spring transaction infrastructure (PlatformTransactionManager/ org.springframework.transaction.ReactiveTransactionManager). Derives from the TransactionAspectSupport class which contains the integration with Spring’s underlying transaction API. TransactionInterceptor simply calls the relevant superclass methods such as invokeWithinTransaction in the correct order. TransactionInterceptors are thread-safe.

它是 AOP 联盟定义的一个 MethodInterceptor ,用于使用常见的 Spring 事务基础结构(PlatformTransactionManager / ReactiveTransactionManager)进行声明式事务管理。 它从 TransactionAspectSupport 类派生,该类包含与 Spring 的基础事务 API 的集成。 TransactionInterceptor 只是以正确的顺序调用相关的超类方法,例如 invokeWithinTransaction 方法。 TransactionInterceptor 是线程安全的。

文档注释中有提到一点,TransactionInterceptor 还有一个父类 TransactionAspectSupport ,这个类中有一些与 SpringFramework 的基础事务 API 的集成(例如执行事务的核心方法 invokeWithinTransaction 、创建事务、提交事务、回滚事务等),这些方法的具体使用,咱放到下一章的事务执行流程中再学习研究。

除此之外,基于 xml 的声明式事务用到的核心 API 就没了,,,没了,,,所以还是非常简单的吧!下面咱继续研究基于注解的声明式事务,它里面都有哪些核心的组件。

2. 基于注解的声明式事务生效原理

基于注解的声明式事务,需要在配置类上标注 @EnableTransactionManagement 注解,之后注册一个事务管理器就 OK 了。同样的,咱还是从这个模块装配的注解开始研究。

2.1 @EnableTransactionManagement

不用打开源码,我们就已经对这个注解的套路了如指掌了,它铁定又 @Import 组件了!当然,源码也确实是这么设计的:

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	boolean proxyTargetClass() default false;
	AdviceMode mode() default AdviceMode.PROXY;
	int order() default Ordered.LOWEST_PRECEDENCE;
}

这个导入的 TransactionManagementConfigurationSelector 咱暂且放在一边,咱先看看这里面的几个属性。

2.1.1 proxyTargetClass

这个属性简单的很,是否直接代理目标类。一看就知道这是关联的 AOP 的东西,AOP 在不主动声明的情况下,是有接口就用 jdk 动态代理,没有接口就用 Cglib 动态代理。如果 proxyTargetClass 属性声明为 true ,则所有被事务增强的类全部使用 Cglib 代理类的方式。

2.1.2 mode

这个属性的取值有两个,分别是 PROXY 和 ASPECTJ ,乍一看我们会认为这两个的底层实现是差不多的,都是代理目标类的对象,在运行期对原有方法添加事务控制的逻辑。然而实际并不是这样,PROXY 代表的确实是运行期增强,但 ASPECTJ 代表的是类加载期增强(类似于 load-time-weaving )。哎这样一说是不是立马就明白了?跟 AOP 一样,我们一般都是使用运行期的动态代理来实现 AOP 或者事务控制,但也可以手动调整通知织入的时机为类加载期。

但是话又说回来了,如果用运行期的动态代理好好地,为啥会再留出一个类加载期的事务控制织入呢?只是为了跟 AOP 迎合吗?这倒不是,设置类加载期的事务控制是为了在 Service 的方法中调用自身类的其他方法考虑的,咱之前学习 AOP 的时候知道,如果代理对象要调用自身类的其他方法,只是用 this 调用的话是不会再次触发通知逻辑执行的,而借助 AopContext 拿到当前代理对象后,再调用自身类的其他方法,就可以再次触发通知逻辑。SpringFramework 的事务增强也是如此,如果使用默认的运行期动态代理织入事务控制的通知,则 Service 中调用其他受事务控制的方法时,不会再次触发事务控制的逻辑,如果使用 ASPECTJ 在类加载器织入事务控制的通知,则该问题可以解决。

不过话说回来,如果要把 mode 改为 ASPECTJ 模式的话,需要额外导入一个依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.framework.version}</version>
</dependency>

除此之外,还需要开启 load-time-weaving ,这个我们实在是不常用,小伙伴们知道一下就好了。

2.1.3 order

这个属性又是很好理解了吧,它指定的是事务通知的执行顺序。前面在 AOP 中我们知道,通知是可以指定执行顺序的,通过 @Order 注解或者 Ordered 接口就可以搞定。此处 @EnableTransactionManagement 帮我们暴露了这个属性的设置,可以说是相当方便了。

2.2 TransactionManagementConfigurationSelector

@EnableTransactionManagement 注解的属性看完了,接下来是它导入的核心选择器了,这个类一看就知道是 ImportSelector ,咱进入源码中来看:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}
}

可见它会根据 @EnableTransactionManagement 注解的 mode 属性,决定导入哪些组件:

  • PROXY – AutoProxyRegistrar + ProxyTransactionManagementConfiguration
  • ASPECTJ – TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME

下面的 TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME 和 JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME 在 spring-tx 包中是找不到的,它们都在 spring-aspects 包下,还是那句话,太不常用,所以小册不深入研究这部分,感兴趣的小伙伴可以自行研究。

咱重点还是看 PROXY 下导入的这两个组件。

2.3 AutoProxyRegistrar

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar

这个家伙又是实现了 ImportBeanDefinitionRegistrar 的,那它的作用肯定是向 BeanDefinitionRegistry 中继续注册新的 BeanDefinition 咯,咱进入核心方法中看:(重要部分已标有注释)

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    for (String annType : annTypes) {
        // 搜寻所有标注的注解
        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (candidate == null) {
            continue;
        }
        // 获取注解上的mode和proxyTargetClass属性
        Object mode = candidate.get("mode");
        Object proxyTargetClass = candidate.get("proxyTargetClass");
        if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
                Boolean.class == proxyTargetClass.getClass()) {
            candidateFound = true;
            // 当mode为PROXY时,会注册额外的BeanDefinition
            if (mode == AdviceMode.PROXY) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                if ((Boolean) proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                    return;
                }
            }
        }
    }
    // logger ......
}

注意看中下部分的那个 if 判断,如果 mode 为 PROXY ,则会向 BeanDefinitionRegistry 中注册额外的 BeanDefinition ,这些 BeanDefinition 想都不用想,肯定是注册与事务控制相关的 AOP 组件了(看工具类的类名也能推测出来嘛)。

咱继续往里深入,看看这个 AopConfigUtils 注册了个啥子东西:

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAutoProxyCreatorIfNecessary(registry, null);
}

public static BeanDefinition registerAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    // 注意类型在这里!
    return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

看下面,它注册了一个类型为 InfrastructureAdvisorAutoProxyCreator 的 bean !这个家伙是干嘛的呢?

2.3.1 InfrastructureAdvisorAutoProxyCreator

翻开它的源码,文档注释似乎已经告诉了我们全部:

Auto-proxy creator that considers infrastructure Advisor beans only, ignoring any application-defined Advisors.

自动代理创建器,仅考虑基础结构 Advisor 类型的 Bean,而忽略任何程序定义的 Advisor 。

从这段简短的 javadoc 中,我们可以得知以下信息:

  • 它是 AOP 的代理创建器
  • 它只会组合基础结构类型的 Advisor ,我们自己写的它一概不理

本来这些信息我们都还算能整明白,但唯独一点:基础结构,这是个啥???这样,小册先上几行代码:

int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;

是不是突然产生了一点点印象?这不就是 BeanDefinition 中的那个 role 属性吗?(org.springframework.beans.factory.support.AbstractBeanDefinition#role)那合着这里面的基础结构,实际上就是指的 ROLE_INFRASTRUCTURE 这个 BeanDefinition 的角色呀。被标注为 ROLE_INFRASTRUCTURE 的 BeanDefinition ,它们通常都是 SpringFramework 内部的 bean ,且在应用程序中起到基础支撑性的作用。而 InfrastructureAdvisorAutoProxyCreator 就是关心这种类型的 BeanDefinition ,那我们在这里就可以先大胆猜测一波:TransactionInterceptor 作为一个 MethodInterceptor ,它被包装为 Advisor 后,role 肯定也是 ROLE_INFRASTRUCTURE 。是不是真的这样呢,咱要继续往下看了。

2.4 ProxyTransactionManagementConfiguration

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration

这个类的头就开始搞事情:这个 ProxyTransactionManagementConfiguration 本身也被标注了 ROLE_INFRASTRUCTURE 的角色,由此也能进一步理解 BeanDefinition 的三种角色了吧。( 有个小细节,SpringFramework 5.2 之前并没有标注这个东东,原因有待考究)

除此之外,还要注意一点:@Configuration(proxyBeanMethods = false) ,它是什么意思呢?这个细节我们在之前的 IOC 章节中没有提到,这里简单说一下:SpringFramework 在解析注解配置类时,会给每个标注了 @Bean 注解的方法都包装一层代理,使这些方法的内部逻辑只能调用一次,第一次逻辑执行完毕后的方法返回值会缓存在代理中,以后再调用这些 @Bean 方法时,会直接返回代理中缓存的返回值。如果在 @Configuration 注解上声明 proxyBeanMethods 属性为 false ,则不会给这些 @Bean 方法构造缓存代理,每次执行方法都会走里面的逻辑了。有关这里面的细节和原理,小册会考虑在正文更新完毕后,在最后追加一些加餐章节讲解。

接下来是这个类注册的组件了,这个类的本体就注册了 3 个,咱逐个来看。

2.4.1 transactionAttributeSource:事务配置源

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
}

没有任何多余的逻辑,只是创建了一个 AnnotationTransactionAttributeSource 就返回了。注意它的类型是 TransactionAttributeSource ,我们先来讲解它。

2.4.1.1 TransactionAttributeSource

这个接口在 SpringFramework 5.2 之前只有一个方法,随着 5.2 之后引入了响应式事务控制,才多了另外一个方法:

public interface TransactionAttributeSource {
	default boolean isCandidateClass(Class<?> targetClass) {
		return true;
	}

	@Nullable
	TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}

下面的 getTransactionAttribute 方法才是重中之重,它可以根据一个类 + 方法,解析转换为 TransactionAttribute ,而 TransactionAttribute 咱前面在第 60 章事务控制模型中已经讲过,它本身是 TransactionDefinition ,所以我们可以这样理解:TransactionAttributeSource 可以根据一个具体的类中的方法,解析转换为一个 TransactionDefinition ( TransactionAttribute )

借助 IDE ,可以发现它的几个实现类,其中就包含了上面它创建的 AnnotationTransactionAttributeSource ,除此之外还有一些其他的实现,小伙伴们可以自行翻看,小册这里不详细展开,咱主要关注的是它创建的这个 AnnotationTransactionAttributeSource 。

Spring Dao编程原理-Spring事务的生效原理

2.4.1.2 AnnotationTransactionAttributeSource

先看一眼 javadoc 吧:

Implementation of the org.springframework.transaction.interceptor.TransactionAttributeSource interface for working with transaction metadata in JDK 1.5+ annotation format. This class reads Spring’s JDK 1.5+ Transactional annotation and exposes corresponding transaction attributes to Spring’s transaction infrastructure. Also supports JTA 1.2’s javax.transaction.Transactional and EJB3’s javax.ejb.TransactionAttribute annotation (if present). This class may also serve as base class for a custom TransactionAttributeSource, or get customized through TransactionAnnotationParser strategies.

它是 TransactionAttributeSource 接口的实现,用于处理 JDK 1.5+ 事务注解的事务元数据。

这个类会读取 Spring 的 @Transactional 注解,并将相应的 TransactionAttribute 事务属性公开给 Spring 的事务底层件。此外,还支持 JTA 1.2 的 javax.transaction.Transactional 和 EJB3 的 javax.ejb.TransactionAttribute 注解(如果存在的话)。此类也可用作自定义 TransactionAttributeSource 的父类,或通过 TransactionAnnotationParser 的策略进行自定义。

说了这么多,我们只关心一句话:它读取 @Transactional 注解,并封装 TransactionAttribute 。由此可见 AnnotationTransactionAttributeSource 是读取和解析标注有 @Transactional 注解的方法的。

至于里面是怎么解析的,用的什么方式解析,咱同样放到下一章再来研究。

2.4.2 transactionInterceptor:事务拦截器

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
    TransactionInterceptor interceptor = new TransactionInterceptor();
    interceptor.setTransactionAttributeSource(transactionAttributeSource);
    if (this.txManager != null) {
        interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
}

果然,注解声明式事务也是离不开这个 TransactionInterceptor 的,这里同样会以编程式的方式创建。

注意这里它依赖了 TransactionAttributeSource ,而且依赖的方式是使用了方法参数,而不是直接在方法内部调用 transactionAttributeSource() 方法,所以也就不需要给这个配置类代理了。

2.4.3 transactionAdvisor:事务增强器

@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
        TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    advisor.setTransactionAttributeSource(transactionAttributeSource);
    advisor.setAdvice(transactionInterceptor);
    // 取@EnableTransactionManagement的order属性
    if (this.enableTx != null) {
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
}

Advisor 增强器!终于发现增强器本体了,这里它使用的是 BeanFactoryTransactionAttributeSourceAdvisor 作为增强器的落地实现,并组合了 TransactionInterceptor 事务拦截器和 TransactionAttributeSource 事务配置源。咱先看看这个增强器本身的东西,老套路,从 javadoc 开始吧:

Advisor driven by a TransactionAttributeSource, used to include a transaction advice bean for methods that are transactional.

它是由 TransactionAttributeSource 驱动的增强器,用于为开启事务的 bean 的方法增强事务通知。

TransactionAttributeSource 驱动?上面我们只看到了 TransactionInterceptor ,没有看到 Pointcut 的身影,那它是怎么搞定切入点的呢(咱都学过了,猜都能猜到肯定是判断类或者方法上有没有打 @Transactional 注解)?咱还是进到 BeanFactoryTransactionAttributeSourceAdvisor 中看看结构吧。

点进源码,一上来就看到了答案的曙光:

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

	@Nullable
	private TransactionAttributeSource transactionAttributeSource;

	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return transactionAttributeSource;
		}
	};
    // ......

诶?原来是有切入点的啊,只不过这个切入点的类型是以 TransactionAttributeSource 为依据的。。。那看来判断一个 bean 是否可以被事务通知增强,需要拿 TransactionAttributeSource 判断才是啊。哎好巧不巧,TransactionAttributeSource 刚好能根据一个类的一个方法,获取到对应的 TransactionAttribute !貌似这一切都讲的过去了!那事实是不是真的像我们推测的那样呢?

当然没错,翻开 TransactionAttributeSourcePointcut 的核心 matches 方法,会发现它就是拿 TransactionAttributeSource 去根据方法和方法所属类,判断是否有对应的事务定义信息(是否被 @Transactional 注解标注),与咱的推测完全一致。

@Override
public boolean matches(Method method, Class<?> targetClass) {
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

到这里,有关 ProxyTransactionManagementConfiguration 中注册的三个组件就都搞明白了,注意 ProxyTransactionManagementConfiguration 还有个父类,咱继续往上爬。

2.5 AbstractTransactionManagementConfiguration

这个类中还有一个组件的注册:

@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
    return new TransactionalEventListenerFactory();
}

很明显这是事务监听器的工厂,它就是处理前面学到的事务事件监听器的。这里面有两个核心的方法,小伙伴们一看就明白:

@Override
public boolean supportsMethod(Method method) {
    // 支持被@TransactionalEventListener标注的事件监听方法
    return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);
}

@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
    // 根据事件监听的方法,创建实际的事件监听器
    return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);
}

除此之外的内容都不算很重要了,咱就不管了。

【到此为止,咱就把 SpringFramework 中声明式事务的生效原理和底层都扒出来了,可以发现核心还是蛮清晰的:一个 TransactionInterceptor ,以及配合的增强器等组件。下一章,咱以前面做过的例子为参考,来实际的研究声明式事务是如何工作、起作用的】

 

免责声明:
1.本站所有内容由本站原创、网络转载、消息撰写、网友投稿等几部分组成。
2.本站原创文字内容若未经特别声明,则遵循协议CC3.0共享协议,转载请务必注明原文链接。
3.本站部分来源于网络转载的文章信息是出于传递更多信息之目的,不意味着赞同其观点。
4.本站所有源码与软件均为原作者提供,仅供学习和研究使用。
5.如您对本网站的相关版权有任何异议,或者认为侵犯了您的合法权益,请及时通知我们处理。
火焰兔 » Spring Dao编程原理-Spring事务的生效原理