声明式事务(Transactional)的工作原理

  • 1. 前置条件(pre-condition)
  • 2. Transaction自动配置(TransactionAutoConfiguration)
    • 2.1 AutoProxyRegistrar -> InfrastructureAdvisorAutoProxyCreator
      • 2.1.1 postProcessBeforeInstantiation
      • 2.1.2 postProcessAfterInitialization
    • 2.2 注册Advisor -> ProxyTransactionManagementConfiguration
  • 3. 代理类执行事务的逻辑 - TransactionInterceptor
  • 4. 事务同步管理
  • 参考

Spring Boot通过起步依赖和自动配置简化了开发环境的配置,节约了开发成本。但是作为一名技术人员,如果不了解一下底层的实现原理,未免太过肤浅。

声明式事物我们一直在用,但是除了@Transactional注解的使用方式,具体底层是如何支撑的,实现原理是什么你真的知道吗?

1. 前置条件(pre-condition)

要实现Transaction的自动配置,需要执行以下自动配置路径
DataSourceAutoConfiguration -> DataSourceTransactionManagerAutoConfiguration -> TransactionAutoConfiguration

DataSourceAutoConfiguration可以配置数据源、数据库连接池等信息。

DataSourceTransactionManagerAutoConfiguration配置了数据源事物管理器(DataSourceTransactionManager),DataSourceTransactionManager的类图如下,它实现了PlatformTransactionManager接口。

2. Transaction自动配置(TransactionAutoConfiguration)

Transaction自动配置实现了一下功能:

  1. 个性化PlatformTransactionManager,如上述的DataSourceTransactionManager
  2. 基于PlatformTransactionManager创建TransactionTemplate
  3. 通过AOP代理实现事务管理,默认配置下,SpringBoot默认启用的是Cglib事务代理。

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration.EnableTransactionManagementConfiguration

@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class,Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic TransactionManagerCustomizers platformTransactionManagerCustomizers(ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {return new TransactionManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));}@Configuration@ConditionalOnSingleCandidate(PlatformTransactionManager.class)public static class TransactionTemplateConfiguration {private final PlatformTransactionManager transactionManager;public TransactionTemplateConfiguration(PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}@Bean@ConditionalOnMissingBeanpublic TransactionTemplate transactionTemplate() {return new TransactionTemplate(this.transactionManager);}}@Configuration@ConditionalOnBean(PlatformTransactionManager.class)@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)public static class EnableTransactionManagementConfiguration {@Configuration@EnableTransactionManagement(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",havingValue = "false", matchIfMissing = false)public static class JdkDynamicAutoProxyConfiguration {}@Configuration@EnableTransactionManagement(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",havingValue = "true", matchIfMissing = true)public static class CglibAutoProxyConfiguration {}}
}

通过AOP代理实现事务管理通过注解@EnableTransactionManagement实现。
首先,它通过Import注解引入了TransactionManagementConfigurationSelector,它是一个ImportSelector实现。
在代理模式下他会返回 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {/*** Returns {@link ProxyTransactionManagementConfiguration} or* {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}* and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},* respectively.*/@Overrideprotected 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);}
}

2.1 AutoProxyRegistrar -> InfrastructureAdvisorAutoProxyCreator

这是一个ImportBeanDefinitionRegistrar类,它引入了一个类型为InfrastructureAdvisorAutoProxyCreator的BeanDefinition,InfrastructureAdvisorAutoProxyCreator是BeanPostProcessor 的实现类,通过它可以在Bean实例化前后创建AOP代理类。

2.1.1 postProcessBeforeInstantiation

如果指定了个性化TargetSource,会在实例化之前创建代理类。
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;
}

2.1.2 postProcessAfterInitialization

所有的Bean实例化后都会执行postProcessAfterInitialization方法;如果需要则创建代理类。

/*** Create a proxy with the configured interceptors if the bean is* identified as one to proxy by the subclass.* @see #getAdvicesAndAdvisorsForBean*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

判断是否需要创建代理类的逻辑:
具体代码可参考org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)

  1. 获取所有的类型为 Advisor 的Bean Definition 列表,并获取 Bean 实例;
  2. 如果 Advisor 是 IntroductionAdvisor 的子类,通过 ClassFilter 匹配是否需要代理类,如果匹配返回 TRUE;
  3. 如果 Advisor 是 PointcutAdvisor 的子类,通过 ClassFilter 进行匹配,如果不匹配直接返回 FALSE;
  4. 获取目标类以及其所有接口类,校验所有的方法是否满足 MethodMatcher 匹配,只要有满足匹配的方法返回 TRUE;

支持事务的Advisor类为:BeanFactoryTransactionAttributeSourceAdvisor,它是PointcutAdvisor的子类,其注入的切点类为
org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut。

创建代理类的逻辑:新建ProxyFactory对象,设置属性,通过getProxy方法获取代理类。
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}return proxyFactory.getProxy(getProxyClassLoader());
}

创建AopProxy,这里最终创建的是ObjenesisCglibAopProxy,然后调用AopProxy的getProxy方法获取代理类。

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}
}

2.2 注册Advisor -> ProxyTransactionManagementConfiguration

该Configuration类注册了一个类型为BeanFactoryTransactionAttributeSourceAdvisor的BeanDefinition。

该Advisor的主要作用是为所有基于@Transactional注解的方法创建AOP代理,以便增加事务控制,事务控制通过TransactionInterceptor来实现。

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource());advisor.setAdvice(transactionInterceptor());if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor() {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource());if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}
}

3. 代理类执行事务的逻辑 - TransactionInterceptor

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas = getTransactionAttributeSource();final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;}else {final ThrowableHolder throwableHolder = new ThrowableHolder();// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {// A RuntimeException: will lead to a rollback.if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {// A normal return value: will lead to a commit.throwableHolder.throwable = ex;return null;}}finally {cleanupTransactionInfo(txInfo);}});// Check result state: It might indicate a Throwable to rethrow.if (throwableHolder.throwable != null) {throw throwableHolder.throwable;}return result;}catch (ThrowableHolderException ex) {throw ex.getCause();}catch (TransactionSystemException ex2) {if (throwableHolder.throwable != null) {logger.error("Application exception overridden by commit exception", throwableHolder.throwable);ex2.initApplicationException(throwableHolder.throwable);}throw ex2;}catch (Throwable ex2) {if (throwableHolder.throwable != null) {logger.error("Application exception overridden by commit exception", throwableHolder.throwable);}throw ex2;}}
}

4. 事务同步管理

Spring开始事务后会将连接信息保存到ThreadLocal内以备后续处理使用,以此来保证事务的一致性。整个过程通过类TransactionSynchronizationManager来实现。

org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = obtainDataSource().getConnection();if (logger.isDebugEnabled()) {logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");}txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already).if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// Bind the connection holder to the thread.if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, obtainDataSource());txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}
}

参考

spring源码阅读–@Transactional实现原理
深入理解Spring系列之十二:@Transactional是如何工作的

声明式事务(Transactional)的工作原理相关推荐

  1. 框架源码专题:Spring声明式事务Transactional的原理

    文章目录 1. @Transactional的使用 2. spring事务的原理 2.1 开启事务,注册bean的后置处理器和相关bean对象,封装Advisor 2.2 匹配并创建动态代理 2.3 ...

  2. 详解 spring 声明式事务(@Transactional)

    spring事务有2种用法:编程式事务和声明式事务. 编程式事务上一篇文章中已经介绍了,不熟悉的建议先看一下编程式事务的用法. 这篇主要介绍声明式事务的用法,我们在工作中基本上用的都是声明式事务,所以 ...

  3. Spring声明式事务管理实现及原理详解

    目录 1.实现步骤 1.1.配置事务管理器 1.2.启动事务注解 1.3.业务添加注解 2.代码演示 2.1.bean文件 2.2.目标类 2.3.测试类 3.Spring事务属性 3.1.传播行为 ...

  4. 面试一口气说出Spring的声明式事务@Transactional注解的6种失效场景

    一.Spring事务管理的两种方式 事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种. 编程式事务:是指在代码中手动的管理事务的提交.回滚 ...

  5. Spring Boot 声明式事务 @Transactional 的使用

    1.Spring Boot 项目中使用事务 首先使用 @EnableTransactionManagement 注解开启事务支持,然后在需要事务管理的 public 方法上添加注解 @Transact ...

  6. Spring声明式事务@Transactional 注解参数详解

    @Transactional注解中常用参数说明 参数名称 功能描述 readOnly 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false.例如:@ ...

  7. spring中编程式事务与声明式事务

    spring中使用事务有两种方式,一种是编程式,一种是声明式. 编程式事务 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManage ...

  8. spring4声明式事务--01注解方式

    1.在spring配置文件中引入 tx 命名空间 xmlns:tx="http://www.springframework.org/schema/tx" 2.配置事务管理器 < ...

  9. spring--(25)声明式事务

    2019独角兽企业重金招聘Python工程师标准>>> 接上篇配置,在com.test.spring.tx包下加入业务接口和类 ####业务接口类 public interface ...

最新文章

  1. 第一次接觸sbt會遇到的
  2. Python基础教学系列— 基础语法
  3. Android性能系列-渲染篇
  4. 经典C语言程序100例之二九
  5. 小程序“自定义关键词”功能的常见问答
  6. UVa10881 Piotr's Ants【模拟】
  7. java 反射 动态编译_动态编译java源代码和反射调用问题
  8. 计算机一级查找文件名,电脑设置始终搜索文件名和内容的方法
  9. CSS超全笔记(适合新手入门)
  10. 如何使用 Buildah 构建容器镜像
  11. python4k高清图片_第一次接触,尝试用python抓取国外4k高清图像数据,真方便
  12. 网络性能测试工具iperf的安装与使用
  13. [CTSC1999][网络流24题]家园
  14. PostgreSQL的全文检索(一)
  15. sas 结果导出到excel 打不开解决办法
  16. 计算机英语 自我介绍,计算机专业学生面试英语自我介绍
  17. 修改testlink服务器ip,简单明了的TestLink配置手册
  18. 如何在手机浏览器中控制扫描仪做文档扫描
  19. python调用打印机打印pdf_python连接打印机实现打印文档、图片、pdf文件等功能
  20. 关于ABAP批次或生产订单特性值的随记

热门文章

  1. 华为手环 8 / NFC 评测 华为手环 8、NFC 参数
  2. 千万翡翠堆豪宅,抖音最豪达人?持续涨粉的王红权星是谁
  3. redis存储的几种方式
  4. js导航定位指定位置
  5. Linux Audio驱动系列(技巧篇) - tingmix调试抓Log
  6. mysql 添加索引卡死_mysql添加索引,查询反而变慢
  7. 【python】二元一次方程求解python源代码
  8. nginx重启几种方法
  9. 一年一折腾,今年二折腾
  10. 跟测试老兵3天成为APP高手(三)