从零开始学spring源码之xml解析(一):入门

    谈到spring,首先想到的肯定是ioc,DI依赖注入,aop,但是其实很多人只是知道这些是spring核心概念,甚至不知道这些代表了什么意思,,作为一个java程序员,怎么能说自己对号称改变了java生态的spring不了解呢。

    首先说一下spring做了啥,他将我们会频繁用到的javaBean交给spring的容器管理,在bean创建的不同阶段,可以在不同的容器中找到,说到底这些容器就是map缓存,有了spring我们就有了管家,可以过过当老爷的瘾,再也不用担心对象啥时候创建,初始化,销毁的问题了,用就完事了。

    说了这么多废话,还是直接撸源码实在,首先看spring源码入口:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //为容器初始化做准备
            // Prepare this context for refreshing.
            prepareRefresh();

            /*

              1、创建BeanFactory对象
            * 2、xml解析
            *     传统标签解析:bean、import等
            *     自定义标签解析 如:<context:component-scan base-package="com.xiangxue.jack"/>
            *     自定义标签解析流程:
            *         a、根据当前解析标签的头信息找到对应的namespaceUri
            *         b、加载spring所以jar中的spring.handlers文件。并建立映射关系
            *         c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类
            *         d、调用类的init方法,init方法是注册了各种自定义标签的解析类
            *         e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析
            *
            * 3、把解析出来的xml标签封装成BeanDefinition对象
            * */
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

    这一个方法,包含了所有spring的操作,spring的代码果然是美如画,加上trycatch才十几行代码就改变了java的生态。    

prepareRefresh();首先看看这个方法,有兴趣的可以自己看看,就是刷新上下文,设置启动日期之类的。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();我们主要看看这一行代码,进去看看

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //核心方法
        refreshBeanFactory();
        return getBeanFactory();
    }

好像还看不到啥关键的,再看看refreshBeanFactory();

protected final void refreshBeanFactory() throws BeansException {

        //如果BeanFactory不为空,则清除BeanFactory和里面的实例
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());

            //设置是否可以循环依赖 allowCircularReferences
            //是否允许使用相同名称重新注册不同的bean实现.
            customizeBeanFactory(beanFactory);

            //解析xml,并把xml中的标签封装成BeanDefinition对象
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

  1.首先做了判断,如果已经有了BeanFactory,就给他干掉,毕竟一山不容二虎嘛。

  2.其次就是创建BeanFactory,其实里面就是new 了一个DefaultListableBeanFactory对象

  3.设置循环依赖标识(默认就是支持的)

  4.解析xml,封装成BeanDefinition对象(这个对象可是所有bean的胚胎,孕育着所有的bean)

好了,看到这四步应该都知道哪个重要了吧,我们进去看看xml解析是怎么做的:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        //创建xml的解析器,这里是一个委托模式
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());

        //这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);

        //主要看这个方法
        loadBeanDefinitions(beanDefinitionReader);
    }

1.首先用了一记委托模式(很多地方都用到了,就不一一介绍了,平时看源码的时候,稍微注意一点应该很容易发现),将xml解析的工作交给了XmlBeanDefinitionReader,一直往下调用loadBeanDefinitions直到:org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        for (Resource resource : resources) {
            //模板设计模式,调用到子类中的方法
            count += loadBeanDefinitions(resource);
        }
        return count;
    }

这边使用了模板设计模式,点进去任意选择一个类,可以看到都是实现了AbstractBeanDefinitionReader类的,选择xml解析实现:

            //获取Resource对象中的xml文件流对象
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //InputSource是jdk中的sax xml文件解析对象
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //主要看这个方法 
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }

获取xml解析对象,继续向下:

            //把inputSource 封装成Document文件对象,这是jdk的API
            Document doc = doLoadDocument(inputSource, resource);

            //主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;

再看如何将bean封装成BeanDefinition对象的:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        //主要看这个方法,createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

又一个委托模式,这是个老板啊,啥都让别人来做,再往下看:registerBeanDefinitions(doc, createReaderContext(resource));一直往下点:

 

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);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        preProcessXml(root);

        //主要看这个方法,标签具体解析过程
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

好像很复杂,好像啥也没干。可以看到preProcessXml和postProcessXml方法里面啥也没有,明显是个钩子方法,主要看parseBeanDefinitions(root, this.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);
        }
    }

默认标签和自定义标签解析还是比较复杂的,看来一篇是讲不完了,下一篇再好好讲讲这一块。

总结:

      写到现在,spring连标签解析都没开始,我们已经看到两个设计模式了,用心去看的话,工厂模式也是到处都在,单例模式贯穿了spring。在spring里面用到了很多设计模式和技巧,我们在看源码的同时,稍稍注意一下这些技巧的使用,并想一想,如果是我们要实现这些功能会怎么做,像spring这样的做法是否比你的想法更好呢,或者,如果你是大牛,你是否有更好的方法去实现spring的这一功能,如果有,去github上提交spring,那你在it界就小有名气了,嘿嘿!

    

posted @ 2020-05-27 15:18  吃肉不长肉的小灏哥  阅读(396)  评论(0编辑  收藏  举报