Spring源码情操陶冶-tx:advice解析器

承接Spring源码情操陶冶-自定义节点的解析。本节关于事务进行简单的解析

spring配置文件样例

简单的事务配置,对save/delete开头的方法加事务,get/find开头的设置为不加事务只读模式

<tx:advice id="txAdvice" transaction-manager="transactionManager">  
        <tx:attributes>  
            <tx:method name="save*" propagation="REQUIRED"/>  
            <tx:method name="delete*" propagation="REQUIRED"/>  
            <tx:method name="get*" read-only="true"/>  
            <tx:method name="find*" read-only="true"/>  
        </tx:attributes>  
</tx:advice>

TxAdviceBeanDefinitionParser解析器

tx:advice节点对应的解析器为TxAdviceBeanDefinitionParser,下面针对该解析器作下详细的解读

实例化对象

直接看复写的getBeanClass()方法

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

TxAdviceBeanDefinitionParser解析器最终解析tx:advice节点为TransactionInterceptor对象

通用的属性集合

    private static final String METHOD_ELEMENT = "method";

    private static final String METHOD_NAME_ATTRIBUTE = "name";

    private static final String ATTRIBUTES_ELEMENT = "attributes";

    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";

针对上述的属性,我们可以看下其中的具体解析

doParse()-解析tx:advice节点

源码端上

    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        // 解析transaction-manager属性对应的bean ref名,默认名为transactionManager
        builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
        
        // 解析子节点tx:attributes
        List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
        if (txAttributes.size() > 1) {
            parserContext.getReaderContext().error(
                    "Element <attributes> is allowed at most once inside element <advice>", element);
        }
        else if (txAttributes.size() == 1) {
            // Using attributes source.
            Element attributeSourceElement = txAttributes.get(0);
            // 解析tx:attribute集合
            RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
            builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
        }
        else {
            // 注册解析器用于解析注解@Transactional
            builder.addPropertyValue("transactionAttributeSource",
                    new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
        }
    }

对于@Transactional方式的解析我们不在此处展开,我们先看下通用的parseAttributeSource()方法解析tx:attribute集合,其会被包装为NameMatchTransactionAttributeSource.class对象。源码如下

private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
        // 解析tx:method节点
        List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
        ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
            new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
        transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
        
        // 
        for (Element methodEle : methods) {
            // 解析name属性,其可符合ant-style模式.包装成TypedStringValue对象
            String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
            TypedStringValue nameHolder = new TypedStringValue(name);
            nameHolder.setSource(parserContext.extractSource(methodEle));
            
            // 解析propagation、isolation、timeout、read-only属性
            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)) {
                attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
            }
            if (StringUtils.hasText(isolation)) {
                attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
            }
            if (StringUtils.hasText(timeout)) {
                try {
                    attribute.setTimeout(Integer.parseInt(timeout));
                }
                catch (NumberFormatException ex) {
                    parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
                }
            }
            if (StringUtils.hasText(readOnly)) {
                attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
            }

            // 解析rollback-for、no-rollback-for属性
            List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
            if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
                String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
                addRollbackRuleAttributesTo(rollbackRules,rollbackForValue);
            }
            if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
                String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
                addNoRollbackRuleAttributesTo(rollbackRules,noRollbackForValue);
            }
            attribute.setRollbackRules(rollbackRules);

            transactionAttributeMap.put(nameHolder, attribute);
        }
        
        // 最后包装成NameMatchTransactionAttributeSource对象,存放上述的配置
        RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
        attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
        attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
        return attributeSourceDefinition;
    }

代码很简单,都是解析属性的,不过还是对上述的一些配置作下白话的总结


  • name 支持ant-style语法,即匹配对应的方法,比如save*,匹配saveUser()/save()等方法

  • propagation 事务传播方式,对应spring的TransactionDefinition接口类常量
    • required 对应PROPAGATION_REQUIRED,对当前的方法判断如果不存在事务,则创建事务。默认配置
    • required_new 对应PROPAGATION_REQUIRED_NEW,对当前方法判断如果存在事务,则创建新事务,待方法执行完毕后恢复事务;反之创建新事务,让方法运行在新事务环境下。即当前方法将运行在独立的新事务下
    • supports 对应PROPAGATION_SUPPORTS,对当前方法判断如果存在事务,则加入该事务;反之则让方法处于非事务状态执行
    • not_spported 对应PROPAGATION_NOT_SUPPORTED,对当前方法判断如果存在事务,则挂起该事务,等方法执行完毕后,再恢复事务。即当前方法不需要事务支持
    • mandatory 对应PROPAGATION_MANDATORY,对当前方法判断如果存在事务,则加入该事务;反之不能新建事务,且抛出异常。即必须处于事务下运行
    • never 对应PROPAGATION_NEVER,对当前方法判断如果存在事务,则抛异常;反之正常运行。即必须在非事务下运行
    • nested 对应PROPAGATION_NESTED,可嵌入式的事务。
  • isolation 事务隔离级别,对应spring的TransactionDefinition接口类常量
    • default 对应ISOLATION_DEFAULT,不作隔离要求,可能会导致dirty read/unrepeatable read/phantom read
    • read_uncommitted 对应JDBC Connection的TRANSACTION_READ_UNCOMMITTED ,可能会导致dirty read/unrepeatable read/phantom read
    • read_committed 对应JDBC Connection的TRANSACTION_READ_COMMITTED,可能会导致unrepeatable read/phantom read
    • reaptable_read 对应JDBC Connection的TRANSACTION_REPEATABLE_READ,可能会导致phantom read
    • serializable 对应JDBC Connection的TRANSACTION_SERIALIZABLE,最安全但最耗性能
      其中关于脏读、不可重复读、幻读的概念见引文https://blog.csdn.net/wang379275614/article/details/24818397。另附言博主对不可重复读、幻读的理解

      扫描二维码关注公众号,回复: 923570 查看本文章

      两者均是在同一事务中会出现的情况,执行的条件均一样。但不可重复读关心返回的数据是否一致,而幻读关心返回的数据条数是否一致

  • timeout 超时参数,单位为s。其只应用于事务传播方式为Required/Required_new,默认为-1

  • read-only 是否配置事务只读,默认为false

  • rollback-for 异常回滚策略配置,即出现何种异常进行回滚,可配置多个异常,支持,分隔。注意此处的配置的异常名也符合ant-style模式

  • no-rollback-for 异常不回滚策略配置,即出现何种异常不进行回滚,可配置多个异常,支持,分隔。注意此处的配置的异常名也符合ant-style模式


猜你喜欢

转载自www.cnblogs.com/question-sky/p/9056649.html