Spring事务管理之AOP方法


使用AOP完成Spring事务管理

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true" propagation="REQUIRED" />
            <!-- rollback-for只对检查型异常适用 这里一般为自定义的Exception 继承Exception父类 因为RuntimeException和Error,Spring默认进行回顾,我们在源码分析中可以看到这一点 -->
            <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />

        </tx:attributes>
    </tx:advice>
    <!-- 适用切面来添加适用处理的功能 -->
    <aop:config>
        <aop:pointcut expression="execution(* wangcc.service.impl.*.*(..))"
            id="serviceMethod" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />

    </aop:config>

当我们使用AOP完成Spring事务管理的时候,我们在处理业务逻辑的时候只需要关心业务逻辑的处理,而Spring事务会通过动态代理的方式来增强需要使用事务的方法来完成Spring事务。

AOP完成Spring事务管理源码分析

tx:advice节点的解析

我们观看上面的XML文件配置,很明显tx:advice这个节点生成的Bean是来完成Spring事务管理功能的,在AOP中充当增强方法处理的作用。而aop:pointcut节点就很明显是指定到底在哪些类的那些方法中执行这个方法增加,即Spring事务管理。

对于AOP,我们已经分析过其源码了,不过当时分析的是aop:pointcut与aop:aspect的组合。而这里aop:aspect换成了aop:advisor。我们知道在分析aop源码的时候aop:aspect节点下的aop:before,aop:after等节点都会传化为AspectJPointcutAdvisor类型的Bean,并且其中包含一个AbstractAspectJAdvice的子类类型的Bean,而且这些子类除了AspectJMethodBeforeAdvice(之后进行了一层封装后实现接口),都直接实现了MethodInterceptor接口,而最后我们需要的就是实现了MethodInterceptor接口的类。

那么我们在分析tx:advice节点的时候,显然可以想象得到他的最终产物也对应着一个Interceptor。

事实也是如此,我们找到解析该节点的类TxAdviceBeanDefinitionParser。找到getBeanClass方法,你会发现BeanClass对应着是TransactionInterceptor,而他也是实现了MethodInterceptor接口的一个类。

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

我们来看下TxAdviceBeanDefinitionParser的声明

class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser {
public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {

我们可以看到直接实现BeanDefinitionParser接口的是他祖先类AbstractBeanDefinitionParser类。

  • parse

    @Override
    public final BeanDefinition parse(Element element, ParserContext parserContext) {
        AbstractBeanDefinition definition = parseInternal(element, parserContext);
    //...省略了一坨代码,主要是将得到的Bean定义注册到Spring容器中,并做一些处理。不是关注的重点
        return definition;
    }

    在parse方法中,重点是parseInternal方法的实现,也就是如何得到Bean定义。而这个方法在AbstractBeanDefinitionParser中是个抽象方法。具体的实现在AbstractSingleBeanDefinitionParser中。

  • parseInternal

    @Override
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        //看是否有parent
        String parentName = getParentName(element);
        if (parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }
        //getBeanClass留给子类实现
        //得到该Bean的Class,这里就是之前说的TransactionInterceptor
        Class<?> beanClass = getBeanClass(element);
        //将Class注册到Bean定义中
        if (beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        }
        else {
            //getBeanClassName留给子类实现,但是这里TxAdviceBeanDefinitionParser并没实现
            String beanClassName = getBeanClassName(element);
            if (beanClassName != null) {
                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
            }
        }
        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        if (parserContext.isNested()) {
            // Inner bean definition must receive same scope as containing bean.
            builder.setScope(parserContext.getContainingBeanDefinition().getScope());
        }
        if (parserContext.isDefaultLazyInit()) {
            // Default-lazy-init applies to custom bean definitions as well.
            builder.setLazyInit(true);
        }
        //对Bean定义进行一系列的参数设置之后,终于来到解析节点,这个方法也是给子类实现的。
        doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }

    在对Bean定义进行了一系列参数的设置之后,调用doParse对节点进行解析。

  • doParse

@Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
      //注入transcationManager属性,如果在配置中没有配置transaction-manager属性,那么这时Spring配置文件中,事务管理器的Bean的Id必须是transcationManager。
        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);
            RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
          //用得到的attributeSourceDefinition注入transactionAttributeSource属性
            builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
        }
      //如果没有显式的配置这个节点,还是需要注入transactionAttributeSource属性,这里给出一个默认的AnnotationTransactionAttributeSource
        else {
            // Assume annotations source.
            builder.addPropertyValue("transactionAttributeSource",
                    new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
        }
    }

我们可以看到doParse方法主要就是为TransactionInterceptor这个类注册了两个属性,transactionAttributeSource和transactionManager。

我们再看看当有显式配置tx:attributes节点时对transactionAttributeSource属性的配置。

    RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
  • parseAttributeSource
    private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
      //得到所有的tx:method节点
        List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
      //创建一个Map,用来存放TypedStringValue(name属性值的包装类)为key,RuleBasedTransactionAttribute为Value的键值对
      //即每一个method name都对应着相应TransactionAttribute
        ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
            new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
        transactionAttributeMap.setSource(parserContext.extractSource(attrEle));

        for (Element methodEle : methods) {
          //得到tx:method中name属性的属性值
            String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
          //将属性值封装成TypedStringValue类型
            TypedStringValue nameHolder = new TypedStringValue(name);
            nameHolder.setSource(parserContext.extractSource(methodEle));

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

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

        RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
        attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
        attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
        return attributeSourceDefinition;
    }

这个方法很长,但是并不难理解。

就是如果在tx:method节点中有对Methodname显式的指定事务传播属性,事务级别,只读,超时等属性,就把他加入到RuleBasedTransactionAttribute中。没有显式指定的话就使用默认的。然后将所有的method节点的信息都存入到NameMatchTransactionAttributeSource中。

那么到这里,我们就分析完了tx:advice节点了。

他主要就是创建了类型为TransactionInterceptor的Bean,并且注入了这个Bean的两个属性,transactionAttributeSource和transactionManager。而当有tx:method节点的时候,transactionAttributeSource属性对应的类型是NameMatchTransactionAttributeSource,如果没有,则对应AnnotationTransactionAttributeSource。

aop:config节点的解析

关于aop:config节点的解析,我们在讲解Spring AOP实现的时候已经说过了,但是当时讲的是aop:aspect和aop:pointcut的组合,现在我们要讲解aop:pointcut和aop:advisor的组合。

我们直接看到ConfigBeanDefinitionParser中解析节点的方法。

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        CompositeComponentDefinition compositeDef =
                new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
        parserContext.pushContainingComponent(compositeDef);

        configureAutoProxyCreator(parserContext, element);

        List<Element> childElts = DomUtils.getChildElements(element);
        for (Element elt: childElts) {
            String localName = parserContext.getDelegate().getLocalName(elt);
            if (POINTCUT.equals(localName)) {
                parsePointcut(elt, parserContext);
            }
            else if (ADVISOR.equals(localName)) {
                parseAdvisor(elt, parserContext);
            }
            else if (ASPECT.equals(localName)) {
                parseAspect(elt, parserContext);
            }
        }

        parserContext.popAndRegisterContainingComponent();
        return null;
    }
  • parseAdvisor

    private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
        AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
        String id = advisorElement.getAttribute(ID);
    
        try {
            this.parseState.push(new AdvisorEntry(id));
            String advisorBeanName = id;
            if (StringUtils.hasText(advisorBeanName)) {
                parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
            }
            else {
                advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
            }
    
            Object pointcut = parsePointcutProperty(advisorElement, parserContext);
            if (pointcut instanceof BeanDefinition) {
                advisorDef.getPropertyValues().add(POINTCUT, pointcut);
                parserContext.registerComponent(
                        new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
            }
            else if (pointcut instanceof String) {
                advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
                parserContext.registerComponent(
                        new AdvisorComponentDefinition(advisorBeanName, advisorDef));
            }
        }
        finally {
            this.parseState.pop();
        }
    }

    方法不难理解,就是将pointcut和advice-ref,也就是之前我们分析的那个节点对应的Bean,TranscationInterceptor关联起来得到DefaultBeanFactoryPointcutAdvisor,然后注入到Spring容器中。

到这里,我们的切面就完成了,然而最重要的是怎么调用我们这个切面。

运用AOP实现的事务管理的调用过程

具体怎么注册拦截器,怎么创建代理类的过程请看AOP源码解析部分。

    @Override
    public Object invoke(final 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, new InvocationCallback() {
            @Override
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
    }

invokeWithinTransaction

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {

        // If the transaction attribute is null, the method is non-transactional.
        final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass);

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

    //省略了CallbackPreferringPlatformTransactionManager这一块,这一块基本就是编程式事务实现事务管理的逻辑
    }

猜你喜欢

转载自blog.csdn.net/BryantLmm/article/details/79275587