spring-transaction源码分析(2)EnableTransactionManagement注解

概述(Java doc)

该注解开启spring的注解驱动事务管理功能,通常标注在@Configuration类上面用于开启命令式事务管理或响应式事务管理。

@Configuration
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public FooRepository fooRepository() {
        // configure and return a class having @Transactional methods
        return new JdbcFooRepository(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // configure and return the necessary JDBC DataSource
    }

    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

The example above can be compared to the following Spring XML configuration:

<beans>
    <tx:annotation-driven/>

    <bean id="fooRepository" class="com.foo.JdbcFooRepository">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="com.vendor.VendorDataSource"/>

    <bean id="transactionManager" class="org.sfwk...DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>
</beans>

添加该注解之后,spring-tx会注册必要的spring组件,这些组件支持注解驱动的事务管理,例如TransactionInterceptor和基于proxy(jdk/cglib)或aspectJ的advice,当调用@Transactional标注的方法时,这些advice将通过拦截器被调用。

注解属性

proxyTargetClass

boolean proxyTargetClass() default false;

设置为true时,创建基于子类的(CGLIB)代理。设置为false时,使用JDK Proxy创建代理。默认值为false。

该属性仅在mode属性设置为AdviceMode.PROXY时生效。

将此属性设置为true将影响所有需要代理的spring bean,而不仅仅是那些用@Transactional标记的bean。

例如,标记有@Async注解bean将同时升级为子类(CGLIB)代理,这个特性实际上没有负面影响,除非明确期望使用某种类型的代理。

mode

/**
 * PROXY: JDK/CGLIB proxy-based advice
 * ASPECTJ: AspectJ weaving-based advice
 */
AdviceMode mode() default AdviceMode.PROXY;

指明使用哪种代理方式嵌入事务通知。

默认AdviceMode.PROXY模式。

PROXY模式只允许通过代理对象调用。同一个类中的本地调用(即this.方法名方式)不能被拦截,本地调用时Transactional注解不会生效,因为spring aop在拦截逻辑执行之后使用原始bean对象调用目标方法,所以this.方法名方式调用会使Transactional注解失效。如果要解决这个问题,可以考虑将其切换到AdviceMode.ASPECTJ。

如果设置为AdviceMode.ASPECTJ模式,proxyTargetClass属性的值将被忽略。在这种情况下,需要依赖spring-aspects模块,该模块又依赖了AspectJ,AspectJ会在编译或加载时将事务拦截逻辑应用到@Transactional标记的类。在这种情况下没有代理,本地调用也将被拦截。

order

// Integer.MAX_VALUE
int order() default Ordered.LOWEST_PRECEDENCE;

指示在特定joinpoint应用多个通知时执行事务通知的顺序。

EnableTransactionManagement源码

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;
}

TransactionManagementConfigurationSelector

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);
	}
}

PROXY模式

EnableTransactionManagement注解的mode属性设置为PROXY模式(默认)时,会Import两个组件:

  • AutoProxyRegistrar
  • ProxyTransactionManagementConfiguration

AutoProxyRegistrar

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;
	if (mode == AdviceMode.PROXY) {
		// 注册InfrastructureAdvisorAutoProxyCreator组件
		AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
		if ((Boolean) proxyTargetClass) {
			// 设置proxyTargetClass为true
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			return;
		}
	}
}

AutoProxyRegistrar会向容器注册InfrastructureAdvisorAutoProxyCreator组件,InfrastructureAdvisorAutoProxyCreator继承了AbstractAutoProxyCreator,用于创建Advisor代理,但是它只考虑基础架构Advisor bean,会忽略@Aspect组件。

若需要支持@Aspect组件,需要使用@EnableAspectJAutoProxy注解开启AspectJ支持,EnableAspectJAutoProxy注解会注册AnnotationAwareAspectJAutoProxyCreator组件,AnnotationAwareAspectJAutoProxyCreator也继承了AbstractAutoProxyCreator,支持Advisor和@Aspect组件。这个内容在之前的AOP源码分析中记录过,此处不再展开分析。

参考AOP源码分析:

https://blog.csdn.net/xuguofeng2016/article/details/128114972

AnnotationAwareAspectJAutoProxyCreator的优先级比InfrastructureAdvisorAutoProxyCreator高,所以当同时注册时,会使用AnnotationAwareAspectJAutoProxyCreator作为Advisor代理创建器。

继承关系:
spring-transaction源码分析(2)EnableTransactionManagement注解

ProxyTransactionManagementConfiguration

注入事务通知相关组件:

  • BeanFactoryTransactionAttributeSourceAdvisor - 实现了PointcutAdvisor接口

  • TransactionInterceptor - 实现了MethodInterceptor接口

Advisor、Advice、Pointcut是spring aop的三组件,aop中已重点分析过,此处不再记录。

ASPECTJ模式

如果EnableTransactionManagement注解的mode属性设置为ASPECTJ模式,会导入AspectJTransactionManagementConfiguration组件。

该模式需要依赖spring-aspects模块。

AspectJTransactionManagementConfiguration

org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration

@Configuration class that registers the Spring infrastructure beans necessary to enable AspectJ-based annotation-driven transaction management for Spring's own org.springframework.transaction.annotation.Transactional annotation.

这个类会装配AnnotationTransactionAspect对象,AnnotationTransactionAspect是一个原生AspectJ组件,该组件使用原生AspectJ在类加载阶段为目标方法嵌入事务拦截逻辑以实现事务管理。

在启动时需要添加以下参数:

-javaagent:path/to/aspectjweaver-${version}.jar

AspectJ参考资料

https://javadoop.com/post/aspectj

https://www.eclipse.org/aspectj/docs.php

原文链接:https://www.cnblogs.com/xugf/p/17376562.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring-transaction源码分析(2)EnableTransactionManagement注解 - Python技术站

(0)
上一篇 2023年5月6日
下一篇 2023年5月6日

相关文章

  • 一篇文章带你搞懂Java线程池实现原理

    下面将从以下几个方面详细讲解Java线程池的实现原理: 线程池介绍 线程池是Java多线程中的一种重要机制,其主要作用包括控制并发线程数量、复用线程、管理并发任务等。线程池是一种节约线程创建和销毁所带来的开销的一种方案,可以避免重复创建和销毁线程,提高应用程序的性能和稳定性。 Java线程池通常由一个线程池管理器和一组工作线程组成,线程池管理器负责线程池的创…

    Java 2023年5月18日
    00
  • 微信小程序云开发 搭建一个管理小程序

    下面是关于“微信小程序云开发搭建一个管理小程序”的完整攻略,希望能对你有帮助。 一、前置条件 搭建微信小程序云开发的管理小程序需要以下几个前置条件: 已经有微信小程序的AppID,并且已经在微信公众平台上进行了配置。 了解基础的微信小程序开发和云开发知识。 安装了微信开发者工具,并且已经登录了自己的微信小程序开发者账号。 二、创建云开发环境 在微信开发者工具…

    Java 2023年5月23日
    00
  • 吊打Java面试官!整理了一周的Spring面试大全(附答案)

    首先,需要明确的是,本文的标题与内容存在一定的误导性和不规范的倾向,建议我们在平时的写作中避免使用类似“吊打”的语言,保持语言的温和和规范。 其次,本文是一份关于Spring面试题的整理和答案的文档,其中包含了很多有用的信息和答案,可以供想要准备Spring面试的人们借鉴。 接下来,我将详细讲解这份攻略的完整分析过程。 标题 首先,我们需要明确标题的含义和规…

    Java 2023年5月19日
    00
  • 浅谈SpringMVC国际化支持

    接下来我将详细讲解“浅谈SpringMVC国际化支持”的完整攻略,包括以下内容: 什么是SpringMVC国际化支持 如何使用SpringMVC国际化支持 示例说明:如何在SpringMVC中实现国际化 什么是SpringMVC国际化支持 SpringMVC国际化支持是一种用于支持跨地区和语言的Web应用程序的技术,它可以将Web应用程序的文本信息本地化,以…

    Java 2023年5月16日
    00
  • Java数组越界问题实例解析

    Java数组越界问题实例解析 在Java中,数组越界问题是一个非常常见的错误,它很容易被忽略,但却会导致程序崩溃。在本篇文章中,我们将讨论如何避免数组越界问题以及如何解决它。 什么是数组越界 在Java中,数组是一组连续的内存空间,用于存储相同类型的数据。数组中每个元素的位置由一个下标(index)来表示,下标从0开始,并递增。因此,如果访问数组时使用的下标…

    Java 2023年5月26日
    00
  • Java如何实现密码加密

    Java实现密码加密的方法有很多种,常用的包括MD5加密、SHA加密、AES加密、DES加密等,下面分别进行详细讲解。 1. MD5加密 MD5是哈希加密的一种,可以将任意长度的数据转换为固定长度的数据。Java提供了MessageDigest类来支持MD5加密,示例代码如下: import java.security.MessageDigest; impo…

    Java 2023年5月19日
    00
  • Java虚拟机最多支持多少个线程的探讨

    Java虚拟机最多支持多少个线程的探讨 Java虚拟机(JVM)是一种能够在不同操作系统上运行Java程序的虚拟机,它的主要功能是将Java字节码转换为计算机可执行代码。在Java程序中,线程(Thread)是用来实现多任务处理的最基本单元,线程的数量对于程序执行的效率和性能有着至关重要的作用。 JVM的线程数量上限 JVM的线程并发数量并不是无限的,它受到…

    Java 2023年5月19日
    00
  • 使用java.nio.file 库优雅的操作文件详解

    使用Java.nio.file库可以实现优雅而高效的文件操作。Java.nio.file库提供了几个主要的类和方法,包括Path、Files和FileSystem等。下面详细讲解如何使用这些类和方法来完成文件操作。 Path类 Path类代表一个文件或目录的路径。可以通过Paths类的静态方法获取一个Path对象,例如: Path path = Paths.…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部