spring事务(二) 声明式事务

spring事务(二) 声明式事务

知识导读

  • 声明式事务是对编程式事务的包装

  • 声明式事务通过使用AOP来实现,注册了一个Advisor类型的对象,创建AOP代理的时候会使用该Advisor

  • Advisor中切点的判断方法是是否能在目标方法上解析获取到事务配置信息 即 @Transaction

  • Advisor的通知拦截器是TransactionInterceptor,在该类中会使用事务管理器 TransactionManager 在目标方法执行前开启事务获取TransactionStatus,然后调用目标方法,执行成功后使用 TransactionManager.commit(TransactionStatus),执行异常后判断异常类型执行TransactionManager.rollback(TransactionStatus),实际上是一个环绕通知。

  • 事务管理的异常回滚机制rollbackfor是在TransactionInterceptor实现的,在回滚的时候会根据事务配置来判断当前异常是该回滚还是提交

声明式事务

spring中开启声明式事务有两种方式,一种是通过xml配置,一种是通过注解开启

使用xml配置,首先开启自动代理,然后配置事务advisor

<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true" />
<!--声明通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/>
        <tx:method name="find*" read-only="true" propagation="NOT_SUPPORTED"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<!--声明aop增强-->
<aop:config>
    <aop:pointcut expression="execution(* exam.service..*.*(..))" id="transaction"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="transaction"/>
</aop:config>

使用注解就是在配置类上添加@EnableTransactionManagement注解

@EnableTransactionManagement
public class config{}

两种启动声明式事务的方式,底层都是在spring容器中注册三个bean,通知(TransactionInterceptor),切点(TransactionAttributeSource)、增强advisor(BeanFactoryTransactionAttributeSourceAdvisor)

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
  //增强 advisor
   @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;
   }
}

在spring aop分析中已经提到过,spring会扫描spring容器中所有类型为Advisor的bean用于进行代理时增强。接下来看下BeanFactoryTransactionAttributeSourceAdvisor中的切点 Pointcut和通知TransactionInterceptor的实现逻辑

切点

在BeanFactoryTransactionAttributeSourceAdvisor中声明了切点,创建了TransactionAttributeSourcePointcut类型的切点

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

接着看TransactionAttributeSourcePointcut中定义的classFilter和methodMatcher实现逻辑

  • ClassFilter.TRU,ClassFilter拦截所有类

  • 实现了MethodMatcher的matches方法,该方法中会调用TransactionAttributeSource解析获取目标方法和目标类上配置的事务属性,如果解析到则进行拦截增强,否则无需代理增强

private ClassFilter classFilter = ClassFilter.TRUE;
//MethodMatcher
@Override
public boolean matches(Method method, Class<?> targetClass) {
  //过滤掉 spring事务的基础类
  if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
      PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
      PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
    return false;
  }
  //获取上面注册的bean  AnnotationTransactionAttributeSource
  TransactionAttributeSource tas = getTransactionAttributeSource();
//使用AnnotationTransactionAttributeSource解析解析方法上的注解@Transaction属性,如果有则需要进行事务增强
  return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

以下是TransactionAttributeSource的继承图,TransactionAttributeSource用于获取目标方法和目标类上配置的TransactionAttribute。主要逻辑由AbstractFallbackTransactionAttributeSource实现

image

在 AbstractFallbackTransactionAttributeSource主要调用computeTransactionAttribute解析获取TransactionAttribute,然后将结果进行了缓存。

public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   if (method.getDeclaringClass() == Object.class) {
      return null;
   }
   //首先从缓存中获取 事务属性
   Object cacheKey = getCacheKey(method, targetClass);
   TransactionAttribute cached = this.attributeCache.get(cacheKey);
   if (cached != null) {
      if (cached == NULL_TRANSACTION_ATTRIBUTE) {
         return null;
      } else {
         return cached;
      }
   } else {
      //缓存没有信息,调用computeTransactionAttribute解析 TransactionAttribute
      TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
      //将解析到的 TransactionAttribute 缓存起来
      if (txAttr == null) {
         this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
      } else {
         String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
         if (txAttr instanceof DefaultTransactionAttribute) {
            ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
         }
         this.attributeCache.put(cacheKey, txAttr);
      }
      return txAttr;
   }
}

AbstractFallbackTransactionAttributeSource.computeTransactionAttribute中会首先去实现类的方法上找事务配置,找到返回。如果没有再去实现类上找事务配置,找到返回。如果都没有再去接口的方法找事务配置,找到返回。如果没有再去接口上找事务配置,找到返回。如果都找不到,返回null,当前切点不拦截目标方法

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   // Don't allow no-public methods as required.
   if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
   }
   Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
   //首先解析获取方法上配置的事务,如果解析到直接返回
   TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
   if (txAttr != null) {
      return txAttr;
   }
  //方法上未解析到事务配置,再从类上获取事务配置,如果解析到返回
   txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
   if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
   }
   //当方法是接口的时候,先从实现类找,实现类找不到再在接口上找
   if (specificMethod != method) {
      //在接口方法上找事务属性
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
         return txAttr;
      }
      //在接口类上找事务属性
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
         return txAttr;
      }
   }
   return null;
}

调用子类AnnotationTransactionAttributeSource的实现逻辑获取事务属性,然后接着调用TransactionAnnotationParser来解析获取@Transaction注解

protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
   return determineTransactionAttribute(clazz);
}

protected TransactionAttribute findTransactionAttribute(Method method) {
   return determineTransactionAttribute(method);
}

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
        for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
            TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
            if (attr != null) {
                return attr;
            }
        }
        return null;
}

spring中定义了TransactionAnnotationParser接口用于解析获取 @Annotation注解中的属性。

image

SpringTransactionAnnotationParser 会解析 @Transaction注解中配置的属性,然后再封装为TransactionAttribute对象返回

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
  //读取 @Transactional 注解上的属性
   AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
         element, Transactional.class, false, false);
   if (attributes != null) {
     //解析注解属性
      return parseTransactionAnnotation(attributes);
   } else {
      return null;
   }
}

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
   RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
   Propagation propagation = attributes.getEnum("propagation");
   rbta.setPropagationBehavior(propagation.value());
   Isolation isolation = attributes.getEnum("isolation");
   rbta.setIsolationLevel(isolation.value());
   rbta.setTimeout(attributes.getNumber("timeout").intValue());
   rbta.setReadOnly(attributes.getBoolean("readOnly"));
   rbta.setQualifier(attributes.getString("value"));

   List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
   for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
      rollbackRules.add(new RollbackRuleAttribute(rbRule));
   }
   for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
      rollbackRules.add(new RollbackRuleAttribute(rbRule));
   }
   for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
      rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
   }
   for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
      rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
   }
   rbta.setRollbackRules(rollbackRules);

   return rbta;
}

至此当从目标方法中解析获取到事务配置信息TransactionAttribute,则当前切点满足拦截目标方法,构建代理类的时候会将Advisor对应的通知TransactionInterceptor添加到拦截器列表中。当代理类方法执行的时候会调TransactionInterceptor的invoke方法进行代理增强。

通知

TransactionInterceptor实现了MethodInterceptor接口,是一个AOP中的通知类,构造参数中需要一个事务管理器和事务配置属性。

当BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut满足切目标方法时,会生成代理类,将当前TransactionInterceptor添加到拦截器链中,目标方法执行的时候会调用其invoke方法。接着调用invokeWithinTransaction实现目标方法的事务管理功能

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
   public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
      setTransactionManager(ptm);
      setTransactionAttributeSource(tas);
   }
   @Override
   @Nullable
   public Object invoke(MethodInvocation invocation) throws Throwable {
      //获取目标类
      Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
      //调用父类 TransactionAspectSupport 的方法逻辑进行事务管理
      return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
   }
}

调用父类TransactionAspectSupport.invokeWithinTransaction实现开启事务逻辑

  1. 获取目标方法的事务配置
  2. 获取spring容器中注册的事务管理器PlatformTransactionManger
  3. 开启事务
  4. 调用目标方法逻辑
  5. 成功提交事务,异常回滚事务
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {
   //获取事务配置信息
   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)) {
     //开启事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      //环绕通知
      Object retVal;
      try {
        //调用 MethodInvocation.invoke,即调用被代理对象的原有逻辑
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
        //执行异常,根据回滚异常配置决定是提交事务还是回滚事务
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      } finally {
        //完成之后清除事务信息
         cleanupTransactionInfo(txInfo);
      }
     //执行完成提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
   } 
  //此处省略代码....
}

获取事务管理器,如果@Transaction中transactionManager属性声明了该方法事务指定的事务管理器,则根据beanName获取指定的TransactionManager,否则获取spring容器中默认声明的PlatformTransactionManager类型的事务管理器

protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
   // Do not attempt to lookup tx manager if no tx attributes are set
   if (txAttr == null || this.beanFactory == null) {
      return getTransactionManager();
   }
  //如果 @Transaction中声明了transactionManager则获取指定名称的事务管理器
   String qualifier = txAttr.getQualifier();
   if (StringUtils.hasText(qualifier)) {
      return determineQualifiedTransactionManager(this.beanFactory, qualifier);
   }
   else if (StringUtils.hasText(this.transactionManagerBeanName)) {
      return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
   }
   else {
      PlatformTransactionManager defaultTransactionManager = getTransactionManager();
      if (defaultTransactionManager == null) {
         defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
         if (defaultTransactionManager == null) {
            defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
            this.transactionManagerCache.putIfAbsent(
                  DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
         }
      }
      return defaultTransactionManager;
   }
}

开启事务,其实就是调用 PlatformTransactionManager.getTransaction开启事务,获取TransactionStatus,然后再封装到TransactionInfo中返回

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
      @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

   // If no name specified, apply method identification as transaction name.
   if (txAttr != null && txAttr.getName() == null) {
      txAttr = new DelegatingTransactionAttribute(txAttr) {
         @Override
         public String getName() {
            return joinpointIdentification;
         }
      };
   }
  //调用PlatformTransactionManager.getTransaction开启事务,并返回事务状态TransactionStatus
   TransactionStatus status = null;
   if (txAttr != null) {
      if (tm != null) {
         status = tm.getTransaction(txAttr);
      } else {
        //无需开启
      }
   }
   return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

提交事务,调用TransactionManager.commit(TransactionStatus)进行事务提交

protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
   if (txInfo != null && txInfo.getTransactionStatus() != null) {
      //调用 TransactionManager.commit 提交事务
      txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
   }
}

异常事务处理,在这里多一步处理,需要判断事务异常配置,如果当前异常需要回滚则调用TransactionManager.rollback(TransactionStatus)进行事务回滚。如果当前异常无需回滚则调用TransactionManager.commit(TransactionStatus)进行事务提交

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
   if (txInfo != null && txInfo.getTransactionStatus() != null) {
      //判断当前异常是否需要回滚,如果需要,调用 TransactionManager.rollback回滚事务
      if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
         try {
            txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
         }
         //此处省略代码....
      } else {
        //如果当前事务无需回滚,调用 TransactionManager.commit 提交事务
         try {
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
         }
         //此处省略代码....
      }
   }
}

判断回滚异常的默认实现,默认只会滚RuntimeException类型的异常

@Override
public boolean rollbackOn(Throwable ex) {
   return (ex instanceof RuntimeException || ex instanceof Error);
}

至此,spring通过AOP环绕通知实现了对编程式事务管理的封装,通过声明式实现了自动事务管理功能。

常见问题

一、在同一类中一个调用本类中另一个有事务的方法,事务是无效

第一步:首先在spring的配置文件中加入以下配置

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true" />

第二步:将之前使用普通调用的方法,换成使用代理调用

((TestService)AopContext.currentProxy()).testTransactional2();

或者直接使用手动回滚

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

二、spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作

1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容