Spring 事务源码(1)—<tx:advice/>事务标签源码解析

  基于最新Spring 5.x,详细介绍了Spring 事务源码,包括<tx:advice/>标签源码解析。

  此前我们学习了Spring事务的基本使用:Spring 5.x 学习(11)—@Transactional Spring 事务的深入学习与使用【两万字】。现在我们来深入学习它的源码,主要是声明式事务的源码。可以说,全网都很难再找到这么深入的Spring事物的源码讲解了。

  在通过本系列文章学习Spring事务的源码之前,我们应该对Spring AOP的通用源码、原理有所了解,这个我们在此前就讲过了,因此在事物的源码中对于某些重复的AOP方法的原理不会赘述!和通用Spring AOP源码的讲解一样,我们从基于XML的事务标签开始解析,后续再讲解事务注解的解析。

Spring 事务源码 系列文章

Spring 5.x 源码汇总

Spring 事务源码(1)—<tx:advice/>事务标签源码解析

Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析

Spring 事务源码(3)—@EnableTransactionManagement事务注解源码解析

Spring 事务源码(4)—BeanFactoryTransactionAttributeSourceAdvisor注解事务通知器源码解析

Spring 事务源码(5)—TransactionInterceptor事务拦截器与事务的AOP增强实现

Spring 事务源码(6)—createTransactionIfNecessary处理事务属性并尝试创建事务【两万字】

Spring 事务源码(7)—事务的completeTransactionAfterThrowing回滚、commitTransactionAfterReturning提交以及事务源码总结【一万字】

1 基于XML的声明式事务解析入口

  在此前“Spring IoC容器初始化源码(3)—parseDefaultElement、parseCustomElement解析标签,registerBeanDefinition注册Bean定义【三万字】”的文章中,我们说过,对于基于XML的扩展标签的解析是在parseCustomElement方法中完成的,不同扩展标签的解析,是根据该标签的本地名称去从NamespaceHandlerSupport的parsers缓存中获取对应的BeanDefinitionParser解析器来完成的。

  对于tx命名空间下的系列的标签的解析器,都是通过TxNamespaceHandler注册到parsers缓存中的,从该类中我们能知道所有tx系列标签及其子标签的解析器:

/**
 * tx命名空间处理器
 */
public class TxNamespaceHandler extends NamespaceHandlerSupport {
    /**
     * 注册一些列的BeanDefinitionParser,用于解析<tx:advice/>、<tx:annotation-driven/>标签及其子标签
     */
    @Override
    public void init() {
        registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
    }
}

  其中TxAdviceBeanDefinitionParser用于解析<tx:advice/>标签,该标签用于定义一个AOP事务通知,AnnotationDrivenBeanDefinitionParser用于解析<tx:annotation-driven/>标签,该标签用于开始事务注解的支持!我们主要讲解这两个标签。

2. < tx:advice/>标签解析

  <tx:advice/>标签由TxAdviceBeanDefinitionParser这个解析器来单独解析,该解析器的uml类图如下:
在这里插入图片描述
  它继承了AbstractSingleBeanDefinitionParser,如果看过此前的< context:property-placeholder/>标签以及PropertySourcesPlaceholderConfigurer占位符解析器源码深度解析的文章就知道这个体系。AbstractSingleBeanDefinitionParser是单个bean定义解析规则的抽象实现,它的parseInternal方法基于建造者模式和模版方法模式,提供了通过BeanDefinitionBuilder以及标签元素的各种属性构建一个GenericBeanDefinition的骨干实现,不同标签的自有属性的解析是通过内部的doParse方法解析的,这个方法由具体的解析器子类实现。
  因此,我们重点讲解TxAdviceBeanDefinitionParser子类重写的方法!

2.1 getBeanClass获取bean定义的class类型

  该方法用于返回构建的bean定义的类型,默认返回null,但是子类TxAdviceBeanDefinitionParser重写了该方法,默认返回TransactionInterceptor.class。也就是说,<tx:advice/>标签将会被解析为一个TransactionInterceptor类型的bean定义。
  TransactionInterceptor,直译过来就是“事务拦截器”,它是个MethodInterceptor的实现,即Advice(通知)。从这里也能看出来,Spring事务的底层仍然采用Spring AOP的机制来实现的。Spring AOP功能的真正的实现就是靠的这个拦截器形成的拦截器链。TransactionInterceptor拦截器也是Spring处理事务最为核心的部分,执行方法的时候就是靠这个拦截器来实现事务控制的,不过现在还不会调用它,我们后面会着重讲解。

@Override
protected Class<?> getBeanClass(Element element) {
   return TransactionInterceptor.class;
}

2.2 doParse解析自有属性

  <tx:advice/>标签的自有属性的解析是通过内部的doParse方法解析的,这个方法由具体的解析器子类实现。
  对于TxAdviceBeanDefinitionParser子类来说,该方法做了以下几件事:

  1. 设置bean定义的transactionManager属性,即事务管理器的beanName,如果没有手动设置标签的"transaction-manager"属性,那么默认设置为"transactionManager"。
  2. 解析<tx:advice/>标签内部的<tx:attributes/>子标签,将会被解析为一个class类型为TransactionAttributeSource的bean定义,即事务属性,并且会设置给transactionAttributeSource属性。
    1. 如果有多个<tx:attributes/>子标签,那么抛出异常:Element <attributes> is allowed at most once inside element <advice>
    2. 如果有一个<tx:attributes/>子标签,则解析该标签,并且bean定义的实际class类型为NameMatchTransactionAttributeSource。主要就是为了支持基于XML配置的事务管理的事务属性源。
    3. 如果没有任何<tx:attributes/>子标签,则同样会创建一个bean定义,实际class类型默认为AnnotationTransactionAttributeSource。该事务属性源主要是为了支持基于注解的事务管理的事务属性源,可以解析@Transactional事务注解。
/*TxAdviceBeanDefinitionParser的属性*/

private static final String ATTRIBUTES_ELEMENT = "attributes";

/**
 * TxAdviceBeanDefinitionParser的方法
 * <p>
 * 解析<tx:advice/>标签的自有属性和子标签
 *
 * @param element       当前<tx:advice/>标签元素
 * @param parserContext 解析上下文,包含一些常用的属性
 * @param builder       bean定义的构建者,TransactionInterceptor类型
 */
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    /*
     * 设置transactionManager属性,即设置与当前advice关联的TransactionManager的beanName
     * 如果没有设置"transaction-manager"属性,则默认设置为"transactionManager"
     */
    builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));

    /*
     * 解析<tx:advice/>标签内部的<tx:attributes/>子标签
     * <tx:attributes/>子标签可以定义方法和事务属性的映射
     *
     * 实际会设置一个transactionAttributeSource属性,值为一个RootBeanDefinition,class类型为TransactionAttributeSource
     * TransactionAttributeSource类似于TargetSource,内部包装了一个或多个TransactionAttribute
     * 而TransactionAttribute又是此前学习过的TransactionDefinition的子接口
     *
     * 因此TransactionAttribute内部同样定义了事务的一系列基础属性,内部具有获取各种Spring事务属性的方法
     * 比如Propagation、Isolation、Timeout、Read-only status等等属性
     */
    //获取<tx:advice/>标签内部的所有<tx:attributes/>子标签集合
    List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
    //如果具有超过一个该类型的子标签,则抛出异常:Element <attributes> is allowed at most once inside element <advice>
    //这说明一个<tx:advice/>标签内部的只能由一个<tx:attributes/>子标签
    if (txAttributes.size() > 1) {
        parserContext.getReaderContext().error(
                "Element <attributes> is allowed at most once inside element <advice>", element);
    }
    //如果具有一个该类型的子标签,则解析该标签
    else if (txAttributes.size() == 1) {
        //获取该标签
        Element attributeSourceElement = txAttributes.get(0);
        /*
         * 解析为一个bean定义,class类型为NameMatchTransactionAttributeSource类型
         * 该事务属性源可以根据方法名匹配,然后将事务的属性作用在匹配的方法上,主要用于支持基于XML配置的事务管理的事务属性源
         */
        RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
        //设置为transactionAttributeSource属性
        builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
    }
    //如果没有设置该类型的子标签
    else {
        /*
         * 那么同样会设置transactionAttributeSource属性,bean定义的class类型默认为AnnotationTransactionAttributeSource类型
         * 该事务属性源是基于注解驱动的事务管理的事务属性源,可以解析@Transactional事务注解
         */
        builder.addPropertyValue("transactionAttributeSource",
                new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
    }
}

2.1.1 getTransactionManagerName获取事务管理器beanName

  该方法获取与当前advice关联的TransactionManager的beanName,如果没有设置<tx:advice/>标签标签的"transaction-manager"属性的值,默认返回"transactionManager"。

/*TxNamespaceHandler的属性*/

static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";

static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";

/**
 * TxNamespaceHandler的属性的方法
 * <p>
 * 获取与当前advice关联的TransactionManager的beanName
 * 如果没有设置"transaction-manager"属性的值,默认返回"transactionManager"
 *
 * @param element 当前标签元素
 * @return transaction-manager属性的值
 */
static String getTransactionManagerName(Element element) {
    //是否设置名为"transaction-manager"的属性,如果设置了,那么返回设置的值,否则返回默认值,即"transactionManager"
    return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
            element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
}

2.1.2 parseAttributeSource解析事务属性源

  该方法用于解析<tx:advice/>标签内部的<tx:attributes/>子标签,将会被解析为一个class类型为NameMatchTransactionAttributeSource的事物属性源的bean定义。
  其内部的<tx:method>子标签将会被解析为RuleBasedTransactionAttribute,它是TransactionAttribute的实现并扩展了DefaultTransactionAttribute。该实现实际上就是对rollback-for和no-rollback-for属性的支持,可以自定义回滚与不回滚的规则。如果没有指定相关规则,那么和DefaultTransactionAttribute一样,将会在抛出RuntimeException和Error时回滚。

/*TxAdviceBeanDefinitionParser的属性*/

private static final String METHOD_ELEMENT = "method";

private static final String METHOD_NAME_ATTRIBUTE = "name";

private static final String TIMEOUT_ATTRIBUTE = "timeout";

private static final String READ_ONLY_ATTRIBUTE = "read-only";

private static final String PROPAGATION_ATTRIBUTE = "propagation";

private static final String ISOLATION_ATTRIBUTE = "isolation";

private static final String ROLLBACK_FOR_ATTRIBUTE = "rollback-for";

private static final String NO_ROLLBACK_FOR_ATTRIBUTE = "no-rollback-for";


/**
 * 解析属性源
 *
 * @param attrEle       <tx:attributes/>子标签元素
 * @param parserContext 解析上下文
 * @return 解析结果,一个bean定义
 */
private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
    //获取<tx:attributes/>标签内部的<tx:method>子标签集合
    List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
    //建立一个map,即这些<tx:method>子标签集合将会统一解析为一个map属性
    ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
            new ManagedMap<>(methods.size());
    transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
    //解析每一个<tx:method>子标签
    for (Element methodEle : methods) {
        //获取name属性
        String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
        TypedStringValue nameHolder = new TypedStringValue(name);
        nameHolder.setSource(parserContext.extractSource(methodEle));
        /*
         * 创建一个RuleBasedTransactionAttribute,它是TransactionAttribute的实现并扩展了DefaultTransactionAttribute
         * 该实现实际上就是对rollback-for和no-rollback-for属性的支持,可以自定义回滚与不回滚的规则。
         * 如果没有指定相关规则,那么和DefaultTransactionAttribute一样,将会在抛出RuntimeException时回滚
         */
        RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
        //传播行为
        String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
        //隔离级别
        String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
        //超时时间
        String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
        //是否只读
        String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
        if (StringUtils.hasText(propagation)) {
            //设置传播行为属性,在XML中设置的是传播行为常量名的字符串后缀,这里会加上传播行为常量名的前缀
            //最终会按照TransactionDefinition中定义的传播行为常量名称获取、设置对应的int值
            //默认事务传播行为是PROPAGATION_REQUIRED,即加入到当前事务或者开始新事物
            attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
        }
        if (StringUtils.hasText(isolation)) {
            //设置隔离级别属性,在XML中设置的是隔离级别常量名的字符串后缀,这里会加上隔离级别常量名的前缀
            //最终会按照TransactionDefinition中定义的隔离级别常量名称获取、设置对应的int值
            //默认事务隔离级别是ISOLATION_DEFAULT,即采用连接的数据的隔离级别
            attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
        }
        if (StringUtils.hasText(timeout)) {
            try {
                //设置超时时间属性,必须是int类型的常量,单位秒
                //默认超时时间为TIMEOUT_DEFAULT,即不超时
                attribute.setTimeout(Integer.parseInt(timeout));
            } catch (NumberFormatException ex) {
                parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
            }
        }
        if (StringUtils.hasText(readOnly)) {
            //设置是否只读
            //默认false,即读写
            attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
        }
        //回滚规则列表
        List<RollbackRuleAttribute> rollbackRules = new LinkedList<>();
        //如果存在rollback-for属性
        if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
            //获取该属性
            String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
            //设置回滚规则
            addRollbackRuleAttributesTo(rollbackRules, rollbackForValue);
        }
        //如果存在no-rollback-for属性
        if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
            //获取该属性
            String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
            //设置不回滚规则,它们在回滚规则之后设置
            //因此如果rollback-for和no-rollback-for设置了相同的异常,那么最终会会回滚
            addNoRollbackRuleAttributesTo(rollbackRules, noRollbackForValue);
        }
        //设置回滚规则,这就是RuleBasedTransactionAttribute的特性
        attribute.setRollbackRules(rollbackRules);
        //存入map中,key为name属性(方法名),value为RuleBasedTransactionAttribute类型的事务属性
        transactionAttributeMap.put(nameHolder, attribute);
    }
    //新建RootBeanDefinition,class为NameMatchTransactionAttributeSource
    //也就是根据方法名匹配,然后该事务属性就会作用在对应的方法上。简单的说就是XML的事务配置的支持
    RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
    attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
    //设置nameMap属性,就是我们刚才解析的<tx:method>子标签集合
    attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
    return attributeSourceDefinition;
}


/**
 * 设置回滚规则属性
 *
 * @param rollbackRules    回滚规则列表
 * @param rollbackForValue rollback-for回滚属性值
 */
private void addRollbackRuleAttributesTo(List<RollbackRuleAttribute> rollbackRules, String rollbackForValue) {
    //根据","字符串拆分为数组
    String[] exceptionTypeNames = StringUtils.commaDelimitedListToStringArray(rollbackForValue);
    for (String typeName : exceptionTypeNames) {
        //添加RollbackRuleAttribute
        rollbackRules.add(new RollbackRuleAttribute(StringUtils.trimWhitespace(typeName)));
    }
}

/**
 1. 设置不回滚规则属性
 2.  3. @param rollbackRules      回滚规则列表
 4. @param noRollbackForValue no-rollback-for不回滚属性值
 */
private void addNoRollbackRuleAttributesTo(List<RollbackRuleAttribute> rollbackRules, String noRollbackForValue) {
    //根据","字符串拆分为数组
    String[] exceptionTypeNames = StringUtils.commaDelimitedListToStringArray(noRollbackForValue);
    for (String typeName : exceptionTypeNames) {
        //添加NoRollbackRuleAttribute,它们在回滚规则之后设置
        rollbackRules.add(new NoRollbackRuleAttribute(StringUtils.trimWhitespace(typeName)));
    }
}

2.1.3 TransactionAttribute事务属性体系

  实际上,我们在此前学习Spring事务管理的使用的时候就介绍了事物定义的顶级接口——TransactionDefinition,它的内部定了一些获取事务属性的的方法,并且提供了事务属性常量。
这里我们不会再次介绍TransactionDefinition,而是向下介绍它的一些实现,也就是上面见到的TransactionAttribute事务属性类!

在这里插入图片描述

2.1.3.1 TransactionAttribute

  TransactionAttribute继承了TransactionDefinition,主要是提供了两个新方法,一个getQualifier方法用于根据限定符选择相应的事务管理器来处理事务,另一个rollbackOn用于判断发生的异常是否应该触发事务回滚!
在这里插入图片描述

2.1.3.2 DefaultTransactionDefinition

  DefaultTransactionDefinition是默认的事务定义的实现,它提供了一些事务属性的默认值:
在这里插入图片描述
  以及提供了设置事务属性的各种方法:
在这里插入图片描述

2.1.3.3 DefaultTransactionAttribute

  DefaultTransactionAttribute是默认的事务属性的实现,它提供了getQualifier和rollbackOn方法的默认实现,并且继承了DefaultTransactionDefinition。
  从rollbackOn方法的默认实现就可以很明显的看出来,当抛出RuntimeException或者Error级别的异常时,事务才会回滚:
在这里插入图片描述

2.1.3.4 RuleBasedTransactionAttribute

  RuleBasedTransactionAttribute继承并扩展了父类DefaultTransactionAttribute,主要就是重写了rollbackOn方法,提供了自定义哪些异常回滚、哪些异常不回滚的功能。
  实际上我们在doParse方法的源码中就见过,一个<tx:method/>标签就会被解析为一个RuleBasedTransactionAttribute的实例,实际上一个@Transactional注解最终也会被解析为一个RuleBasedTransactionAttribute,自定义回滚异常的特性就是通过rollback-for以及no-rollback-for属性来配置的。

  对于自定义的回滚异常的完整的匹配规则如下:

  1. 首先会匹配rollbackRules集合,默认情况下,这个集合中的RollbackRuleAttribute回滚规则在前,NoRollbackRuleAttribute不回滚规则在后。NoRollbackRuleAttribute是RollbackRuleAttribute的子类!
    1. 匹配的时候按照顺序遍历rollbackRules并对规则依次匹配,采用“Winning(获胜)规则”,说白了就是最佳匹配原则,它是根据匹配时的栈深度depth来比较的
      1. 对于每一个回滚规则,将会从指定异常(抛出的异常)开始递归的匹配异常,逐级向父类异常匹配,并且返回匹配深度。匹配的规则就是:如果当前异常类型的全路径类名包含当前的回滚规则设置的异常名字符串(也就是配置的异常名),则表示匹配当前规则。
        1. 如果当前异常直接匹配当前的回滚规则,那么depth返回0。
        2. 如果不匹配,那么获取当前异常的父类异常,depth+1,继续递归的匹配父类,如果匹配到了则返回此时的depth。
        3. 如果父类来到了Throwable,即顶级异常接口,并且还没有匹配到,那么说明没有匹配成功,返回-1。
    2. 根据上面的匹配规则,有可能会匹配多个NoRollbackRuleAttribute或者NoRollbackRuleAttribute,最佳匹配原则最终会采用depth最小的那一个规则作为winner。
      1. 如果当前匹配规则的depth和此前的某个规则的depth相等,那么不会采用当前的规则。也就是说,如果如果回滚和不回滚的规则设置了相同类型的异常,虽然它们的depth一致,但是由于默认情况下回滚规则在前先进行匹配,因此将会采用回滚规则作为最匹配的规则!这就是此前学习Spring事务的时候说的“如果回滚和不回滚的规则设置了相同类型的异常,那么在抛出该异常时将会回滚!”的原理。
  2. rollbackRules匹配完毕如果winner还是为null,那么说明和自定义的规则没有任何匹配(无论是回滚还是不回滚规则),那么此时调用父类的方法和默认匹配逻辑,即如果当前异常属于RuntimeException或者Error级别的异常时,事务才会回滚。
    1. 这就是此前学习Spring事务的时候说的“如果抛出的异常没有匹配我们设定的规则匹配,那么仍然会采用默认规则”的原理。
  3. 如果winner不为null,即和某个自定义的规则能够匹配,那么判断当前winner是否不属于NoRollbackRuleAttribute。如果不属于,那么最终返回true,表示需要回滚;如果属于,那么最终返回false,表示不需要回滚。
public class RuleBasedTransactionAttribute extends 
DefaultTransactionAttribute implements Serializable {

    /**
     * 回滚异常字符串的前缀
     */
    public static final String PREFIX_ROLLBACK_RULE = "-";

    /**
     * 不回滚异常字符串的前缀
     */
    public static final String PREFIX_COMMIT_RULE = "+";

    /**
     * 回滚规则的属性集合
     * 根据parseAttributeSource方法的逻辑,回滚规则在集合前面,不回滚规则在集合后面
     */
    @Nullable
    private List<RollbackRuleAttribute> rollbackRules;


    /**
     * 创建一个新的基于规则的事务属性,具有默认设置。
     */
    public RuleBasedTransactionAttribute() {
        super();
    }

    /**
     * 复制构造器。
     */
    public RuleBasedTransactionAttribute(RuleBasedTransactionAttribute other) {
        super(other);
        this.rollbackRules = (other.rollbackRules != null ? new ArrayList<>(other.rollbackRules) : null);
    }

    /**
     * 使用给定的传播行为和指定回滚规则集合创建新的默认事务属性。
     */
    public RuleBasedTransactionAttribute(int propagationBehavior, List<RollbackRuleAttribute> rollbackRules) {
        super(propagationBehavior);
        this.rollbackRules = rollbackRules;
    }


    /**
     * 设置要应用的回滚规则集合
     */
    public void setRollbackRules(List<RollbackRuleAttribute> rollbackRules) {
        this.rollbackRules = rollbackRules;
    }

    /**
     * 获取要应用的回滚规则集合,从不为null
     */
    public List<RollbackRuleAttribute> getRollbackRules() {
        if (this.rollbackRules == null) {
            this.rollbackRules = new LinkedList<>();
        }
        return this.rollbackRules;
    }


    /**
     * 采用"Winning rule"机制来判断对当前异常是否需要进行回滚
     * 这是最简单的规则,即选择异常的继承层次结构中最接近当前异常的规则。
     * <p>
     * 如果需要回滚,则返回true,否则返回false
     */
    @Override
    public boolean rollbackOn(Throwable ex) {
        if (logger.isTraceEnabled()) {
            logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
        }
        //rollbackRules中最匹配的回滚规则,默认为null
        RollbackRuleAttribute winner = null;
        //回滚回滚规则匹配成功时的匹配异常栈深度,用来查找最匹配的那一个回滚规则
        int deepest = Integer.MAX_VALUE;
        //如果rollbackRules回滚规则集合不为null,那么判断回滚规则是否匹配
        if (this.rollbackRules != null) {
            //从前向后遍历,因此回滚规则RollbackRuleAttribute将会先进行匹配,不回滚规则NoRollbackRuleAttribute将会后进行匹配
            //NoRollbackRuleAttribute是RollbackRuleAttribute的子类
            for (RollbackRuleAttribute rule : this.rollbackRules) {
                //根据当前规则获取匹配时的异常栈深度
                int depth = rule.getDepth(ex);
                //如果匹配了当前规则,并且当前的深度小于此前匹配的异常栈深度
                if (depth >= 0 && depth < deepest) {
                    //那么deepest赋值为当前异常栈深度,即找到最匹配的那一个
                    deepest = depth;
                    //winner设置为当前回滚规则实例
                    winner = rule;
                }
            }
        }

        if (logger.isTraceEnabled()) {
            logger.trace("Winning rollback rule is: " + winner);
        }

        //rollbackRules匹配完毕如果winner还是为null,那么说明没有任何匹配,此时调用父类的方法和逻辑
        //即如果当前异常属于RuntimeException或者Error级别的异常时,事务才会回滚
        if (winner == null) {
            logger.trace("No relevant rollback rule found: applying default rules");
            return super.rollbackOn(ex);
        }
        //判断当前winner是否不属于NoRollbackRuleAttribute
        //如果不属于,那么最终返回true,表示需要回滚;如果属于,那么最终返回false,表示不需要回滚
        return !(winner instanceof NoRollbackRuleAttribute);
    }


    @Override
    public String toString() {
        StringBuilder result = getAttributeDescription();
        if (this.rollbackRules != null) {
            for (RollbackRuleAttribute rule : this.rollbackRules) {
                //如果是回滚规则,则使用"-"前缀,如果是不回滚规则,则使用"+"前缀
                String sign = (rule instanceof NoRollbackRuleAttribute ? PREFIX_COMMIT_RULE : PREFIX_ROLLBACK_RULE);
                result.append(',').append(sign).append(rule.getExceptionName());
            }
        }
        return result.toString();
    }
}
2.1.3.4.1 getDepth获取匹配栈深度

  我们来看看它的getDepth方法的逻辑,就能更加明白它的匹配规则了。

/**
 * RollbackRuleAttribute的属性
 * <p>
 * 这个属性就是在rollback-for或者no-rollback-for方法中配置的异常字符串
 * 通常我们配置为简单异常类名并使用","分隔开。
 */
private final String exceptionName;


/**
 * RollbackRuleAttribute的方法
 * <p>
 * 返回异常超类匹配的深度。
 * <p>
 * 0的意思是和当前异常完全匹配的。如果没有匹配则返回-1,否则,返回匹配超类异常的深度
 */
public int getDepth(Throwable ex) {
    //深度默认为0
    return getDepth(ex.getClass(), 0);
}

/**
 * RollbackRuleAttribute的属性
 * <p>
 * 递归的匹配异常,从指定异常开始,逐级向父类异常匹配,并且返回匹配深度
 * <p>
 * 匹配的规则就是如果当前异常类型的全路径类名包含当前的回滚规则的异常名,则表示匹配
 * 如果当前异常直接匹配当前的回滚规则,那么depth返回0
 * 如果不匹配,那么获取当前异常的父类异常,depth+1,继续递归的匹配父类,如果匹配到了则返回此时的depth
 * 如果父类来到了Throwable,即顶级异常接口,并且还没有匹配到,那么说明没有匹配成功,返回-1
 *
 * @param exceptionClass 异常类型
 * @param depth          深度
 * @return 匹配时的深度
 */
private int getDepth(Class<?> exceptionClass, int depth) {
    //如果当前异常类型的全路径类名包含当前的回滚规则的异常名
    //那么说明找到了一个匹配的异常,直接返回depth,递归结束
    if (exceptionClass.getName().contains(this.exceptionName)) {
        // Found it!
        return depth;
    }
    // If we've gone as far as we can go and haven't found it...
    //如果当前异常类型属于Throwable,那么说明没有找到匹配的异常,直接返回-1,递归结束
    if (exceptionClass == Throwable.class) {
        return -1;
    }
    //递归的进行匹配,不过下一次将会匹配当前异常的父类异常,并且匹配深度+1
    return getDepth(exceptionClass.getSuperclass(), depth + 1);
}

2.1.4 TransactionAttributeSource事务属性源体系

在这里插入图片描述
  事物属性源的顶级接口是TransactionAttributeSource,它类似于TargetSource,内部“包装”了一个或者多个TransactionAttribute,它被用来完成某个方法到应用的哪些事务属性的匹配工作。

public interface TransactionAttributeSource {

    /**
     * 来自Spring 5.2的一个新方法
     * <p>
     * 确定给定的类是否是此事务切入点的候选者,即是否有资格继续进行类方法匹配来判断是否能被进行事务增强
     * <p>
     * 如果此方法返回false,则不会遍历给定类上的方法并进行getTransactionAttribute的调用。
     * 因此,该方法是对不受事务影响的类的优化,避免了多次方法级别的校验
     * 而返回true则是意味着该类需要对给定类上的每个方法分别进行全面自省,
     * 来判断是否真正可以被事务代理,而不是真正的可以被此事务通知器行代理。
     *
     * @param targetClass the class to introspect
     * @return 如果已知该类在类或方法级别没有事务属性,则返回false;否则返回false。
     * 默认实现返回true,从而导致常规的方法自省。
     */
    default boolean isCandidateClass(Class<?> targetClass) {
        return true;
    }

    /**
     * 返回给定方法的事务属性,返回null表示该方法无事务。
     *
     * @param method      查找的方法
     * @param targetClass 目标类,可能是null,此时将使用方法反射获取的class
     * @return 匹配的事务属性,如果没有发现则返回null
     */
    @Nullable
    TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);

}

  TransactionAttributeSource的不同实现可以根据不同的源数据获取对应方法的事务属性,获取属性的来源可以是XML配置(<tx:attributes/>)、注解(@Transactional)等地方。
  TransactionAttributeSource通常是被设置为TransactionInterceptor的transactionAttributeSource属性,是在方法拦截的时候进行匹配的。

2.1.4.1 NameMatchTransactionAttributeSource

  TransactionAttributeSource的简单实现。
  在基于XML的Spring AOP配置在解析时时,<tx:attributes/>标签就会被解析为一个NameMatchTransactionAttributeSource。而内部的<tx:method>标签则会被解析为一个个的RuleBasedTransactionAttribute对象,并且最终统一存入NameMatchTransactionAttributeSource的nameMap属性中,一个键值对的key就是<tx:method>的name属性,value就是对应的TransactionAttribute。<tx:method>的name属性支持"xxx*", “*xxx” 和"*xxx*"通配符。
  因此,NameMatchTransactionAttributeSource是通过方法名是否匹配nameMap的key来查找并获取对应的TransactionAttribute的。这里的匹配仅仅是简单的"xxx*", “*xxx” 和"*xxx*"匹配!
在这里插入图片描述

2.1.4.2 MethodMapTransactionAttributeSource

  TransactionAttributeSource的简单实现。
  相比于NameMatchTransactionAttributeSource,它会预先将方法名字符串解析为Method对象并与对应的AttributeSource存入一个缓存map,在后续查找的时候将会直接根据Method对象查找对应的TransactionAttribute,而不会进行方法名通配符的判断!
  虽然在预先解析方法名字符串为对应的Method的时候也能指定通配符,但是实际上还需要指定一个类名,通配符方法的预查找需要在指定类中进行,因此它的判断查找范围更小。
在这里插入图片描述

2.1.4.3 CompositeTransactionAttributeSource

  见其名知其意,它是一种复合的TransactionAttributeSource的实现,内部保存了一个TransactionAttributeSource数组,在调用getTransactionAttribute方法获取TransactionAttribute时,会遍历该数组,尝试从每一个TransactionAttributeSource实现中获取!
在这里插入图片描述

2.1.4.4 MatchAlwaysTransactionAttributeSource

  非常简单的TransactionAttributeSource的实现,在调用getTransactionAttribute方法获取TransactionAttribute时,它始终对所有方法返回同一个的TransactionAttribute,因此这些方法将应用相同的事务属性。这个TransactionAttribute可指定,默认为DefaultTransactionAttribute。
在这里插入图片描述

2.1.4.5 AnnotationTransactionAttributeSource

  基于注解的TransactionAttributeSource的实现。在上面的parseAttributeSource方法源码中,如果<tx:advice/>标签内部没有设置<tx:attributes/>子标签,那么仍然会配置一个AnnotationTransactionAttributeSource类型的bean定义并设置给transactionAttributeSource属性。而@EnableTransactionManagement注解也会配置一个AnnotationTransactionAttributeSource的bean定义。
  在调用AnnotationTransactionAttributeSource的getTransactionAttribute方法获取TransactionAttribute时,可以解析对应方法/类上的事务注解并构建一个TransactionAttribute返回(也会采用缓存),比如Spring的@Transactional注解,JTA 1.2的javax.transaction.Transactional注解,EJB3的javax.ejb.TransactionAttribute注解。
  在内部解析注解的时候,将会委托使用对应的TransactionAnnotationParser解析器,Spring的@Transactional注解使用SpringTransactionAnnotationParser,JTA 1.2的javax.transaction.Transactional注解使用JtaTransactionAnnotationParser,EJB3的javax.ejb.TransactionAttribute注解使用Ejb3TransactionAnnotationParser注解。
  具体的原理,在后面时候到了自然会详细讲解(讲解基于注解的Spring AOP原理的时候)。
在这里插入图片描述

3 小结

  <tx:advice/>标签将会由TxAdviceBeanDefinitionParser解析器来解析,其被封装为一个TransactionInterceptor类型的bean定义,这就是一个AOP的拦截器,Spring 方法级别的声明式事务控制也就是靠这个拦截器实现的。
  transaction-manager属性将被解析并设置为TransactionInterceptor的transactionManagerBeanName属性,默认值为transactionManager。
  <tx:advice/>标签内部可以有一个或者没有<tx:attributes/>子标签,该标签将会被解析为一个TransactionAttributeSource类型bean定义,并被设置为TransactionInterceptor的transactionAttributeSource属性。
  如果有多个<tx:attributes/>子标签,那么抛出异常:Element <attributes> is allowed at most once inside element <advice>
  如果有一个<tx:attributes/>子标签,则解析该标签,并且bean定义的实际class类型为NameMatchTransactionAttributeSource。主要就是为了支持基于XML配置的事务管理的事务属性源。
  如果没有任何<tx:attributes/>子标签,则同样会创建一个bean定义,实际class类型默认为AnnotationTransactionAttributeSource。该事务属性源主要是为了支持基于注解的事务管理的事务属性源,可以解析@Transactional事务注解。
  <tx:attributes/>标签内部的一个<tx:method/>子标签将会被解析为一个RuleBasedTransactionAttribute事务属性对象,支持自定义回滚规则。这些对象及其对应的name(方法名)将会被放到一个map中并存入NameMatchTransactionAttributeSource的nameMap属性中!
  最终,一个<tx:advice/>标签只会向容器注册这一个TransactionInterceptor类型的bean定义,bean定义的id实际上就是<tx:advice/>标签的id属性。

相关文章:
  https://spring.io/
  Spring Framework 5.x 学习
  Spring Framework 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值