Spring事务原理详解

2022-01-05 10:06:00 浏览数 (1)

一、使用

spring事务开启和使用比较简单,需要有数据源和事务管理器,然后在启动门面类上开启事务,在需要使用事务的地方添加注解就可以了,我们简单做一下回顾。

1.配置数据源

代码语言:javascript复制
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://host:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=true
spring.datasource.username=aaa
spring.datasource.password=bbb

2.开启事务能力

代码语言:javascript复制
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class},scanBasePackages = "xxx")
@EnableTransactionManagement
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3.使用事务

代码语言:javascript复制
@Transactional
public void evaluateSatisfaction(Req req) {
  //do business
}

这样我们就可以在业务中使用事务了。

那么我们就要思考一个问题,为什么通过简单的配置和两个注解我们就能便捷的使用事务了,spring框架层面做了哪些能力支撑,接下来我们将从原理和源码维度对spring的事务原理进行分析。

二、原理分析

首先,spring事务管理之所以能生效,得有数据库吧,得有数据源吧,得有事务管理器吧,我们先看一下spring对于数据源和事务管理器的处理和配置。

1.数据源与事务管理器

先看一个配置类DataSourceConfiguration:

代码语言:javascript复制
abstract class DataSourceConfiguration {
  /**
   *  Tomcat/Hikari/DBCP DataSource configuration.
   */
  /**
   * Generic DataSource configuration.
   */
  @ConditionalOnMissingBean(DataSource.class)
  @ConditionalOnProperty(name = "spring.datasource.type")
  static class Generic {
    @Bean
    public DataSource dataSource(DataSourceProperties properties) {
      return properties.initializeDataSourceBuilder().build();
    }
  }
}

省略掉的是Tomcat、Hikari、DBCP数据源配置,国内使用率最高的Druid数据源通过spring.datasource.type指定类型并且自定义配置,然后使用DataSourceProperties和DataSourceBuilder构造。

然后我们看另外一个配置类DataSourceAutoConfiguration:

代码语言:javascript复制
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
    DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

  //...省略

  @Configuration
  @Conditional(PooledDataSourceCondition.class)
  @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
  @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
      DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
      DataSourceJmxConfiguration.class })
  protected static class PooledDataSourceConfiguration {
  }
}

此处PooledDataSourceConfiguration通过@Import注解导入了支持的数据源配置,应用启动时ConfigurationClassPostProcessor会将@Configuration扫描并根据配置数据源类型注册BeanDefinition供后续实例化,这里我们暂且理解为DataSource已经配置好了。

再看另外一个配置类DataSourceTransactionManagerAutoConfiguration:

代码语言:javascript复制
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {

  @Configuration
  @ConditionalOnSingleCandidate(DataSource.class)
  static class DataSourceTransactionManagerConfiguration {

    private final DataSource dataSource;

    private final TransactionManagerCustomizers transactionManagerCustomizers;

    DataSourceTransactionManagerConfiguration(DataSource dataSource,
        ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
      this.dataSource = dataSource;
      this.transactionManagerCustomizers = transactionManagerCustomizers
          .getIfAvailable();
    }
    @Bean
    @ConditionalOnMissingBean(PlatformTransactionManager.class)
    public DataSourceTransactionManager transactionManager(
        DataSourceProperties properties) {
      DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
          this.dataSource);
      if (this.transactionManagerCustomizers != null) {
        this.transactionManagerCustomizers.customize(transactionManager);
      }
      return transactionManager;
    }
  }
}

该类是数据源事务管理配置类,根据前边的数据源创建事务管理器DataSourceTransactionManager,从继承关系可以看出它是一个PlatformTransactionManager(后边会用到此概念):

spring的事务管理也就是使用事务管理器通过数据源和连接来实现开启、提交和回滚操作。

2.开启事务能力

通过@EnableTransactionManagement来开启spring事务能力:

代码语言:javascript复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

  boolean proxyTargetClass() default false;

  AdviceMode mode() default AdviceMode.PROXY;

  int order() default Ordered.LOWEST_PRECEDENCE;

}

注解有三个属性

  • proxyTargetClass:true表示基于CGLIB创建代理,false表示使用jdk动态代理。默认值为false。仅当mode()设置为AdviceMode时适用。
  • mode:表示事务通知实现方式,proxy表示通过代理的当时拦截处理事务,同类级别的方法调用将无法拦截;ASPECTJ表示使用aspectj织入的方式拦截处理事务,功能比proxy模式强大。
  • order:在调用链中有多个通知,事务通知的执行优先级,默认最低。

@EnableTransactionManagement开启的能力委托给TransactionManagementConfigurationSelector来实现:

代码语言:javascript复制
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);
  }
}

它是一个ImportSelector,ConfigurationClassPostProcessor会将其初始化并将selectImports返回的类列注册BeanDefinition然后实例化(可参考《ConfigurationClassPostProcessor原理详解》),父类AdviceModeImportSelector的selectImports方法解析EnableTransactionManagement注解属性,然后调用子类selectImports方法返回导入类列表,我们的分析针对mode=proxy展开,可以看到TransactionManagementConfigurationSelector导入了两个类,AutoProxyRegistrar和ProxyTransactionManagementConfiguration,逐个分析一下,先看AutoProxyRegistrar。

它是一个ImportBeanDefinitionRegistrar,实例化后被调用其registerBeanDefinitions方法:

代码语言:javascript复制
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  boolean candidateFound = false;
  Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
  for (String annoType : annoTypes) {
    AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
    if (candidate == null) {
      continue;
    }
    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) {
        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
        if ((Boolean) proxyTargetClass) {
          AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
          return;
        }
      }
    }
  }
}

会调用AopConfigUtils注册一个类InfrastructureAdvisorAutoProxyCreator,并且需要注意的是,如果有多次调用,只要有任一次proxyTargetClass为true,就会把其该属性设置为true,影响是全局,比如应用中同时开启了事务和异步能力,如果@EnableAsync把proxyTargetClass设置为true那么也会影响到事务以及其他aop能力。

代码语言:javascript复制
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
    BeanDefinitionRegistry registry, @Nullable Object source) {

  return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

这个类非常重要,是实现代理和AOP能力的核心组件,拥有处理代理逻辑的能力,本质上又是一个BeanPostProcessor,他的实现比较简单,核心逻辑在其父类AbstractAdvisorAutoProxyCreator和AbstractAutoProxyCreator中,父类实现了InstantiationAwareBeanPostProcessor接口,其postProcessBeforeInstantiation方法会在bean初始化之前调用,如果返回不为null,那么就不在执行bean的初始化,框架设计的目的就是给出扩展能力来做一些代理和bean实例化短路的事情。从spring事务场景来说,就是对使用了事务的类做代理实现,加入事务能力。 那么我们就看一下AbstractAutoProxyCreator的postProcessBeforeInstantiation实现:

代码语言:javascript复制
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
  Object cacheKey = getCacheKey(beanClass, beanName);

  //...省略

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

先创建TargetSource,然后获取适用于当前bean的通知,然后创建代理并返回。因为TargetSource是spring框架的复杂代理,针对代理只能单个类代理做出的扩展,我们暂时没有用到该能力,所以此处targetSource为null,这里不会执行创建代理操作。从AbstractAutoProxyCreator中看到重写BeanPostProcessor的一个方法postProcessAfterInitialization,这个方法的作用是目标bean实例化之后,初始化的时候可以对齐进行包装甚至替换掉,对于事务来说,可以用把事务能力包装进去,或者用具有事务能力的bean实例替换掉原来的,看一下实现:

代码语言:javascript复制
@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;
}

如果给定的bean有资格被代理,那么调用wrapIfNecessary包装并返回:

代码语言:javascript复制
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  //...省略
    // Create proxy if we have advice.
  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
  }
  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return bean;
}

获取bean使用的通知,然后创建代理,并将通知能力织入进去。因为获取适用当前bean的通知涉及到前边我们说的selectImports导入的另外一个配置类ProxyTransactionManagementConfiguration,所以我们这里只分析创建代理,获取通知和通知拦截逻辑后边分析。先看一下创建代理逻辑:

代码语言:javascript复制
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
    @Nullable Object[] specificInterceptors, TargetSource targetSource) {
  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());
}

先创建代理工厂,然后填充通知器、设置要代理的目标类,然后创建代理。

从代理工厂的继承关系可以看到其实现了Advised接口,并且拥有创建代理的能力,创建代理工厂的时候使用默认无参构造器,会调用父类ProxyCreatorSupport的无参构造器:

代码语言:javascript复制
public ProxyCreatorSupport() {
  this.aopProxyFactory = new DefaultAopProxyFactory();
}

创建代理的任务会委托给DefaultAopProxyFactory执行,会先调用createAopProxy创建生成代理的工具:

代码语言:javascript复制
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    Class<?> targetClass = config.getTargetClass();
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
      return new JdkDynamicAopProxy(config);
    }
    return new ObjenesisCglibAopProxy(config);
  }
  else {
    return new JdkDynamicAopProxy(config);
  }
}

根据proxyTargetClass和接口特性决定使用jdk的动态代理JdkDynamicAopProxy还是cglib的动态代理ObjenesisCglibAopProxy。 两者都实现了AopProxy接口用于创建代理,jdk动态代理也实现了InvocationHandler接口,将其本身也定义成一个代理执行器,代理目标类逻辑执行的时候对调用其invoke方法。jdk动态代理的生成逻辑如下:

代码语言:javascript复制
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
  if (logger.isTraceEnabled()) {
    logger.trace("Creating JDK dynamic proxy: "   this.advised.getTargetSource());
  }
  Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

而cglib则根据目标类生成增强类,并嵌入拦截逻辑,本篇可以理解为把事务能力嵌入:

代码语言:javascript复制
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
  try {
    Class<?> rootClass = this.advised.getTargetClass();
    Class<?> proxySuperClass = rootClass;
    if (ClassUtils.isCglibProxyClass(rootClass)) {
      proxySuperClass = rootClass.getSuperclass();
      Class<?>[] additionalInterfaces = rootClass.getInterfaces();
      for (Class<?> additionalInterface : additionalInterfaces) {
        this.advised.addInterface(additionalInterface);
      }
    }
    // Configure CGLIB Enhancer...
    Enhancer enhancer = createEnhancer();
    if (classLoader != null) {
      enhancer.setClassLoader(classLoader);
      if (classLoader instanceof SmartClassLoader &&
          ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
        enhancer.setUseCache(false);
      }
    }
    enhancer.setSuperclass(proxySuperClass);
    enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

    Callback[] callbacks = getCallbacks(rootClass);
    Class<?>[] types = new Class<?>[callbacks.length];
    for (int x = 0; x < types.length; x  ) {
      types[x] = callbacks[x].getClass();
    }
    // fixedInterceptorMap only populated at this point, after getCallbacks call above
    enhancer.setCallbackFilter(new ProxyCallbackFilter(
        this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
    enhancer.setCallbackTypes(types);

    // Generate the proxy class and create a proxy instance.
    return createProxyClassAndInstance(enhancer, callbacks);
  }
  catch (CodeGenerationException | IllegalArgumentException ex) {
    throw new AopConfigException("Could not generate CGLIB subclass of "   this.advised.getTargetClass()  
        ": Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (Throwable ex) {
    // TargetSource.getTarget() failed
    throw new AopConfigException("Unexpected AOP exception", ex);
  }
}

两种代理方式的逻辑执行此处不做分析,后边执行事务能力的时候分析。 前边有提到生成代理的时候寻找合适的通知会用到另外一个配置类ProxyTransactionManagementConfiguration,我们也分析一下它做了什么事情。

代码语言:javascript复制
@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;
  }
}

它定义了事务属性、事务拦截器和事务通知器等三个bean,并且将事务管理器注入了进来(也可能此刻没有注入),这里重要的是

BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor。

它是一个Advisor,持有切入点(TransactionAttributeSourcePointcut)、事务属性和拦截器。拦截逻辑由TransactionInterceptor实现。

结合前边生成代理的逻辑,我们可以初步猜测,根据通知器中的切入点是否匹配拦截类的方法,如果匹配则将拦截器逻辑嵌入到生成的代理类中。 我们看一下前边提到的创建代理之前,有调用getAdvicesAndAdvisorsForBean方法获取目标bean适用的通知,会调用子类AbstractAdvisorAutoProxyCreator的实现:

代码语言:javascript复制
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
    Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

  List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
  if (advisors.isEmpty()) {
    return DO_NOT_PROXY;
  }
  return advisors.toArray();
}

调用链比较长,省略中间步骤,最终调用到AopUtils的canApply方法来做过滤:

代码语言:javascript复制
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
  if (!pc.getClassFilter().matches(targetClass)) {
    return false;
  }
    //...省略
  Set<Class<?>> classes = new LinkedHashSet<>();
  classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
  for (Class<?> clazz : classes) {
    Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
    for (Method method : methods) {
      if (introductionAwareMethodMatcher != null ?
          introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
          methodMatcher.matches(method, targetClass)) {
        return true;
      }
    }
  }
  return false;
}

由于我们的通知器不是Introduction类型,然后会使用BeanFactoryTransactionAttributeSourceAdvisor持有的TransactionAttributeSourcePointcut来做过滤:

代码语言:javascript复制
@Override
public boolean matches(Method method, Class<?> targetClass) {
  if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
      PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
      PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
    return false;
  }
  TransactionAttributeSource tas = getTransactionAttributeSource();
  return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

然后委托给TransactionAttributeSource来提取目标方法和类是否具有事务标注,如果有@Transactional就认为适用,其实就是用通知器的Pointcut来和方法对比是否匹配适用。上述方法会调用到SpringTransactionAnnotationParser的方法:

代码语言:javascript复制
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
  AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
      element, Transactional.class, false, false);
  if (attributes != null) {
    return parseTransactionAnnotation(attributes);
  }
  else {
    return null;
  }
}

其实就是为了提取方法上是否有Transactional注解。 先看一下寻找合适通知器的时序图:

InfrastructureAdvisorAutoProxyCreator处理器注册与实例化:

为使用事务的目标类生成代理的时序图如下:

3.执行事务逻辑

根据前边的分析,基于@EnableTransactionManagement和@Transactional注解,应用启动后会将目标类生成代理,并将事务能力织入进去,生成代理的方式有两种,分别是jdk动态代理和cglib动态代理,由于编写事务逻辑的方法所属类大多不实现接口,所以对于事务,生成代理的方式是cglib。

cglib生成代理类的方式是继承目标类,重写目标方法,然后把拦截器逻辑嵌入进去,对于事务能力,会调用到TransactionInterceptor拦截器的invoke方法:

代码语言:javascript复制
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
  // Work out the target class: may be {@code null}.
  // The TransactionAttributeSource should be passed the target class
  // as well as the method, which may be from an interface.
  Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

  // Adapt to TransactionAspectSupport's invokeWithinTransaction...
  return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

寻找到目标类,然后执行事务调用invokeWithinTransaction:

代码语言:javascript复制
@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 = null;
    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 exception
      completeTransactionAfterThrowing(txInfo, ex);
      throw ex;
    }
    finally {
      cleanupTransactionInfo(txInfo);
    }
    commitTransactionAfterReturning(txInfo);
    return retVal;
  }

  else {
    //...省略
  }
}

前边有说到,创建的事务管理器DataSourceTransactionManager是PlatformTransactionManager类型,txAttr是从方法上提取的事务注解属性,所以满足if分支,逻辑大概意思是如果需要,则创建事务,然后执行代理类的目标方法逻辑调用,然后如果有异常则做异常回滚并抛出,如果没有异常则提交事务。 先看一下创建事务createTransactionIfNecessary

代码语言:javascript复制
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
    @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    //...省略
  TransactionStatus status = null;
  if (txAttr != null) {
    if (tm != null) {
      status = tm.getTransaction(txAttr);
    }
  }
  return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

从事务管理器中获取事务状态,然后准备事务信息,获取事务状态使用DataSourceTransactionManager的父类重写方法getTransaction:

代码语言:javascript复制
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
  Object transaction = doGetTransaction();

  if (isExistingTransaction(transaction)) {
    // Existing transaction found -> check propagation behavior to find out how to behave.
    return handleExistingTransaction(definition, transaction, debugEnabled);
  }

  // Check definition settings for new transaction.
  if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
    throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
  }

  // No existing transaction found -> check propagation behavior to find out how to proceed.
  if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
    throw new IllegalTransactionStateException(
        "No existing transaction found for transaction marked with propagation 'mandatory'");
  }
  else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
      definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
      definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    SuspendedResourcesHolder suspendedResources = suspend(null);
    if (debugEnabled) {
      logger.debug("Creating new transaction with name ["   definition.getName()   "]: "   definition);
    }
    try {
      boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
      DefaultTransactionStatus status = newTransactionStatus(
          definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
      doBegin(transaction, definition);
      prepareSynchronization(status, definition);
      return status;
    }
    catch (RuntimeException | Error ex) {
      resume(null, suspendedResources);
      throw ex;
    }
  }
  else {
    // Create "empty" transaction: no actual transaction, but potentially synchronization.
    boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
    return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
  }
}

先检查当前线程调用是否处于事务中,如果是则处理线程传播并返回;然后检查事务超时属性,如果事务传播配置是PROPAGATION_MANDATORY且当前无事务,则报错返回;如果事务传播级别是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED则开启事务并返回事务状态。补充一下事务传播级别的概念:

PROPAGATION_REQUIRED:支持当前事务;如果不存在,则创建一个新的。默认

PROPAGATION_SUPPORTS:支持当前事务;如果不存在,以非事务方式执行

PROPAGATION_MANDATORY:支持当前事务;如果当前不存在事务,则异常

PROPAGATION_REQUIRES_NEW:创建一个新事务,如果当前事务存在,挂起当前事务 PROPAGATION_NOT_SUPPORTED:不支持当前事务;始终以非事务方式执行

PROPAGATION_NEVER:不支持事务;如果当前事务存在,则异常

PROPAGATION_NESTED:如果存在当前事务,在嵌套事务中执行

我们看一下开启事务的实现:

代码语言:javascript复制
@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();
      txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
    }
    txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
    con = txObject.getConnectionHolder().getConnection();
    Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
    txObject.setPreviousIsolationLevel(previousIsolationLevel);

    if (con.getAutoCommit()) {
      txObject.setMustRestoreAutoCommit(true);
      con.setAutoCommit(false);
    }
    prepareTransactionalConnection(con, definition);
    txObject.getConnectionHolder().setTransactionActive(true);

    int timeout = determineTimeout(definition);
    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
      txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
    }
    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);
  }
}

如果没有数据库连接则先获取连接,设置隔离级别,然后将自动提交设置false,配置超时时间,然后把数据库连接绑定到线程上。

回到事务拦截器的invokeWithinTransaction方法,拿到事务信息后,执行业务逻辑,如果发生异常则调用执行回滚和后续逻辑,如果执行成功则提交事务。

看一下异常执行逻辑:

代码语言:javascript复制
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
  if (txInfo != null && txInfo.getTransactionStatus() != null) {
    if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
      try {
        txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
      }
      catch (TransactionSystemException ex2) {
        ex2.initApplicationException(ex);
        throw ex2;
      }
      catch (RuntimeException | Error ex2) {
        throw ex2;
      }
    }
    else {
      try {
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
      }
      catch (TransactionSystemException ex2) {
        ex2.initApplicationException(ex);
        throw ex2;
      }
      catch (RuntimeException | Error ex2) {
        throw ex2;
      }
    }
  }
}

如果存在事务,且异常类型符合回滚规则,那么调用事务管理器的回滚逻辑,否则执行提交。回滚最终会调用数据库连接的回滚方法:

代码语言:javascript复制
protected void doRollback(DefaultTransactionStatus status) {
  DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
  Connection con = txObject.getConnectionHolder().getConnection();
  try {
    con.rollback();
  }
  catch (SQLException ex) {
    throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
  }
}

然后会调用后续的一些操作,比如之前我们分析过的《TransactionalEventListener使用场景与原理分析》。如果业务执行成功,那么执行事务提交,事务提交由事务管理器最终委托给数据库连接操作,并且也会触发一些依赖事务执行状态的操作,比如TransactionalEventListener依赖的是事务提交。 到这里事务的执行逻辑分析完了,我们看一下执行时序图:

总结

spring事务能力的支撑用到了很多知识,动态代理、AOP、反射、后置处理器等等,总的来说就是应用启动时为需要使用事务的类生成代理类,以及将事务能力(拦截逻辑)织入进去,在实例化的时候调用后置处理器的逻辑,将代理类实例化替代目标类,并放入上下文容器中,在实际调用目标类事务方法的时候,被代理类中ReflectiveMethodInvocation拦截,然后先调用拦截器中的事务逻辑,然后再调用目标类的业务逻辑,最后处理异常回滚和提交,看起来比较简单,但是框架层面提供了非常庞大的基础组件来支撑和实现事务能力,当然这些基础组件大部分都会复用,比如AOP和动态代理,在异步和缓存场景下都会用到,包括我们自己扩展一些能力出来的时候也会用到。

0 人点赞