spring声明式事务源码剖析(上)

本文深入剖析Spring声明式事务的源码,从解析spring xml文件开始,详细解释如何通过BeanDefinition构建bean数据结构,涉及BeanDefinitionRegistry、DefaultListableBeanFactory、NamespaceHandler等关键组件,重点解析AopNamespaceHandler和TxNamespaceHandler对事务处理的配置。
摘要由CSDN通过智能技术生成

背景

主要目的为了解决如下问题:

spring是如何使用aop来实现声明式事务管理的?

为了解决上面这个问题,需解决如下两个问题:

1.spring如何解析xml文件中关于事务配置标签?
2.代码执行时aop是如何进行事务管理的?

本文主要解决第一个问题。

1.spring如何解析xml文件中关于事务配置标签?

准备spring xml文件解析

对于spring xml文件的解析,我们要分析的代码片断为:

ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("/com/test/spring.xml");

而对应的spring.xml内容片断为:

<aop:config>
    <aop:pointcut id="interceptorPointCuts"
        expression="execution(* com.test..srv.impl.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" />
        <tx:method name="del*" propagation="REQUIRED" rollback-for="Exception" />
        <tx:method name="save*" propagation="REQUIRED"
            rollback-for="Exception" />
        <tx:method name="update*" propagation="REQUIRED"
            rollback-for="Exception" />
        <tx:method name="do*" propagation="REQUIRED" rollback-for="Exception" />
    </tx:attributes>
</tx:advice>

后文主要分析spring如何解析xml文件,并生成相应的bean数据结构BeanDefinition.阅读时,带着一个问题。

spring xml中配置的bean是如何加载进spring容器的?

我们先查看spring解析标签代码,再查看解析标签代码.spring代码版本v.4.2.4.RELEASE.

  1. ClassPathXmlApplicationContext 构造器
  2. ClasspathXmlApplicationContext.refresh()

    这个方法很重要,它是spring启动的整个过程,我们现在只分析它是如何完成bean加载这一步
    // Tell the subclass to refresh the internal bean factory.  
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
  3. AbstractApplicationContext.obtainFreshBeanFactory

    这个方法最后返回一个ConfigurableListableBeanFactory实现的实例,我们所有的bean最终都被装载进这个容器中,那么下面继续.

  4. AbstractApplicationContext.refreshBeanFactory()

  5. AbstractRefreshableApplicationContext.refreshBeanFactory

在这个方法中我们实例化一个DefaultListableBeanFactory实例,它的类定义

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
                        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable

在此需要注意一下BeanDefinitionRegistry这个接口,它的作用即可以动态的向容器注册相应的bean,正是由于这个接口,我们在后面才可以将相应的bean注册到DefaultListableBeanFactory容器实例中。

  1. AbstractRefreshableApplicationContext.loadBeanDefinition

  2. AbstractXmlApplicationContext.loadBeanDefinition(实现)

    将xml文件读取工作转嫁给 XmlBeanDefinitionReader

  3. AbstractXmlApplicationContext.loadBeanDefinitions

    在这里循环读取每个的Resource;

  4. AbstractBeanDefinitionReader.loadBeanDefinitions(String …locations)

  5. AbstractBeanDefinitionReader.loadBeanDefinitions(String location)

  6. AbstractBeanDefinitionReader.loadBeanDefinitions(String location,Set actualResource)

  7. AbstractBeanDefinitionReader.loadBeanDefinitions(Resource… resources)

  8. XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)

  9. XmlBeanDefinitionReader.loadBeanDefinitions(EncodedREsource encodedResource)

    这个方法通过流读取相应的Resource加载相应的bean

  10. XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource,Resource resource)

    使用w3c api解析xml文件,部分代码:
    
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        Document doc = doLoadDocument(inputSource, resource);
        return registerBeanDefinitions(doc, resource);
    }
    // n多异常catch代码省略
    

    }

  11. XmlBeanDefinitionReader.registerBeanDefinitions(Document doc,Resource resource)

将工作转嫁给 BeanDefinitionDocumentReader

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
  1. BeanDefinitionDocumentReader.registerBeanDefinitions(Document doc,XmlReaderContext readerContxt)

  2. DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc,XmlReaderContext readerContxt)

  3. DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element root)

    将工作转嫁给 BeanDefinitionParserDelegate
    
    protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    
    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
    }
    
    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
    
    this.delegate = parent;
    }
    
  4. DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Element root,BeanDefinitionParserDelegate delegate)

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
    }
    

解析则在此分流,对于默认namespace(默认namespace指是的beans),则调用 parseDefaultElement;否则调用delegate.parseCustomElement.由于我们要了解 等事务的配置,所以在此我们跟进delegate.parseCustomElement方法.

  1. BeanDefinitionParserDelegate.parseCustomElement(Element ele)

  2. BeanDefinitionParserDelegate.parseCustomElement(Element ele,BeanDefition containingBd);

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
    

在这个方法,获取相应的namespace的 NamespaceHandler 进行xml文件解析,也正是这个方法解决了spring不同namespace的标签的Handler.现在将工作转嫁给相应的 NamespaceHandler,我现在想了解 是如何解析,所以相应的NamespaceHandler为 AopNamespaceHandler.

  1. AopNamespaceHandler.init()

    在AopNamespaceHandler的init方法,    
        public void init() {
        // In 2.0 XSD as well as in 2.1 XSD.
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    
        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
    对于 config标签,应用ConfigBeanDefinitionParser
    
  2. ConfigBeanDefinitionParser.parse(Element element,ParserContext parserContext)

ConfigBeanDefinitionParser.configureAutoProxyCreator(ParserContext parserContext,Element element)这个方法注入一个AspectJAwareAdvisorAutoProxyCreator bean至DefaultListableBeanFactory实例,它的作用是自动装载aop切面。

对于 标签使用ConfigBeanDefinitionParser.parsePointcut(Element pointcutElement,ParserContext parserContext)方法解析,并注入DefaultListableBeanFactory.同理解析解析,等标签。

同样对于节点,我们可以找到相应的 NamespaceHandler,对于对应的TxNamespaceHandler.

  1. TxNamespaceHandler.init方法

    @Override
    public void init() {
        registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
    }
    
  2. TxAdviceBeanDefinitionParser.parse()
    这个方法实现在其父类AbstractBeanDefinitionParser中

  3. AbstractBeanDefinitionParser.parse()

  4. AbstractBeanDefinitionParser.parseInternal(Element element, ParserContext parserContext)

  5. AbstractSingleBeanDefinitionParser.parseInternal(Element element, ParserContext parserContext)

  6. AbstractSingleBeanDefinitionParser.getBeanClass(Element element)
    这个方法在子类中被重写

  7. TxAdviceBeanDefinitionParser.getBeanClass(Element element)

    @Override
    protected Class<?> getBeanClass(Element element) {
    return TransactionInterceptor.class;
    }
    
    TransactionInterceptor的类定义  
    @SuppressWarnings("serial")
    public class TransactionInterceptor extends TransactionAspectSupport 
    implements MethodInterceptor, Serializable 
    
     我们可看到这是一个环绕通知,关于这个类的内容后续再分析。
    

至此我们已经了解了spring是如何解析xml,并生成相应的aop所需要的bean.这里只是粗略的了解了 spring是如何加载 bean,对于每个bean细细解析,只能自己去看源代码了。下一篇我们详细看一下TransactionInterceptor如何进行事务拦截。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值