美文网首页
Spring源码解析之XmlBeanDefinitionRead

Spring源码解析之XmlBeanDefinitionRead

作者: 突突兔007 | 来源:发表于2020-07-01 23:26 被阅读0次

    我们先来看一段代码

    @Test
        public void testAbc() throws IOException {
            BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
            Person person = (Person) bf.getBean("person");
            person.say();
        }
    

    applicationContext.xml文件内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean name="person"  class="com.yao.Person"></bean>
    </beans>
    

    这段代码非常简单也非常经典,spring是如何解析xml文件将bean注入到spring容器的?过程是什么样的?我们接下看看spring是如何做的,至于是如何将xml解析成Document的,这里先不做过多解释,后面有章节单独介绍?
    我们进入XmlBeanFactory的构造函数中跟进一下可以看到如下代码

    /**
         * Create a new XmlBeanFactory with the given input stream,
         * which must be parsable using DOM.
         * @param resource the XML resource to load bean definitions from
         * @param parentBeanFactory parent bean factory
         * @throws BeansException in case of loading or parsing errors
         */
        public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
            super(parentBeanFactory);
            //解析resource,并将resource解析出的Bean会封成Spring的BeanDefinition,然后在将
            //每一个BeanDefinition在注册到Spring容器中完成初始化
            this.reader.loadBeanDefinitions(resource);
        }
    

    继续跟进loadBeanDefinitions(resource)方法会看到如下

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            //new EncodedResource()完成编码集设置的工作
            return loadBeanDefinitions(new EncodedResource(resource));
        }
    

    继续:

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
            ...
                    //准备加载BeanDefinitions
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            ...
        }
    

    继续,XmlBeanDefinitionReader#doLoadBeanDefinitions的核心到了,就是这里的两个

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
    
            try {
                //完成xml文件读取并转成Document对象,Document对象就包含了xml节点中的所有节点数据
                Document doc = doLoadDocument(inputSource, resource);
                //将读取出来的Document对象进一步解析成Spring的BeanDefinitions并注册到spring容器,完成初始化
                int count = registerBeanDefinitions(doc, resource);
                ...
                return count;
            }
            ...
        }
    

    二、doLoadDocument(inputSource, resource)

    读取xml文件,并最终返回一个Document对象来表示xml里所有元素。

    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
            return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                    getValidationModeForResource(resource), isNamespaceAware());
        }
    

    这里需要说明一下两个参数:

    getEntityResolver()

    记着一点就是该方法免去了从网络上获取dtd或xsd的声明,因为你的xml写的规范不规范是需要dtd和xsd的声明文件做约束的。因为网络是不可靠的,不可需要验证的时候从网络上获取dtd或xsd头文件。所以此参数的作用就是通过此链接地址作为key到对应的文件中去找相应的dtd或xsd文件,这样子就避免了取网络下载,现在这个dtd或xsd文件相应的模块中都有,我们不用担心不存在,而且是越往后的版本里都包含了之前版本的dtd或xsd文件声明。

    getValidationModeForResource(resource)

    记住一点就是这个是根据返回的resource中内容中,通过内容判断我们配置的applicationContext到底是xsd格式还是dtd格式。
    判断的内容依据就是判断我们声明的xml中有没有对应的schema文件声明,并用publichId和SystemId组合表示。比如:判断声明如果有DOCTYPE则就是dtd验证,反之就是xsd验证。

    如何从inputSource中读取文件并解析成docuemnt,这里就不说赘述了,有兴趣的朋友可以看下源码,挺简单的。

    三、registerBeanDefinitions(doc, resource)

    registerBeanDefinitions()方法完成了java bean到 spring bean的一个转换。看看如何做的

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            /**
             *  createBeanDefinitionDocumentReader,此方法会实例化BeanDefinitionDocumentReader接口,使用
             *  BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader,最后该实现类的
             *  registerBeanDefinitions()方法完成了BeanDefinitions的注册,
             */
            //1.BeanDefinitionDocumentReader读取器,用于读取解从xml解析出来的Domcument,用BeanDefinition封装起来
            //这里只是封装,还未注册到spring容器。
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            //2. 这一步获取注册前容器已有的BeanDefinition数量
            int countBefore = getRegistry().getBeanDefinitionCount();
            //3. 这一步才是真正的将读取出来的document对象,封装成的BeanDefinition注册到Spring容器,
            //registerBeanDefinitions()方法使用的的BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中的
            //
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    

    这个时候我们看createBeanDefinitionDocumentReader()这个方法一直跟下去会看到XmlBeanDefinitionReader类中有如下

    private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
                DefaultBeanDefinitionDocumentReader.class;
    

    查看DefaultBeanDefinitionDocumentReader类会找到最终如下方法

    //默认标签的解析
        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
            if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
                importBeanDefinitionResource(ele);
            }
            else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
                processAliasRegistration(ele);
            }
            else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
                //我们看这个解析Bean标签
                processBeanDefinition(ele, delegate);
            }
            else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
                // recurse
                doRegisterBeanDefinitions(ele);
            }
        }
    

    processBeanDefinition(ele, delegate)如下,此方法中完成了Spring Bean的注册

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
            //将从document中解析出来的java Bean 封装成spring的BeanDefinition
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                    // Register the final decorated instance.
                    //将封装好的spring BeanDefinition注册到spring容器中,完成Spring bean的初始化
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch (BeanDefinitionStoreException ex) {
                    getReaderContext().error("Failed to register bean definition with name '" +
                            bdHolder.getBeanName() + "'", ele, ex);
                }
                // Send registration event.
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
            }
        }
    

    四、delegate.parseBeanDefinitionElement(ele)

    我们查看spring是如何将java Bean封装成spring的Bean的

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
            ...
                //我们看parseBeanDefinitionElement
            AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
            if (beanDefinition != null) {
                ...
                String[] aliasesArray = StringUtils.toStringArray(aliases);
                return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
            }
            return null;
        }
    
    public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    
            ...
    
            try {
                //new了一个GenericBeanDefinition,来封装java bean,
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
                //完成属性的解析
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
                //解析子元素meta
                //元数据使用如下:
                //<bean name="person"  class="com.yao.Person">
                //    <meta key="age" value="18"/>
                //</bean>
                //元数据的解析
                parseMetaElements(ele, bd);
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
                //构造函数的解析
                parseConstructorArgElements(ele, bd);
                parsePropertyElements(ele, bd);
                parseQualifierElements(ele, bd);
    
                bd.setResource(this.readerContext.getResource());
                bd.setSource(extractSource(ele));
                //最后返回bd
                return bd;
            }
            ...
            return null;
        }
    

    我们可以看到Spring容器中的BeanDefinition实际都是GenericBeanDefinition类型的。至此Spring的初始化完成了。

    相关文章

      网友评论

          本文标题:Spring源码解析之XmlBeanDefinitionRead

          本文链接:https://www.haomeiwen.com/subject/apskqktx.html