Spring源码解析之XmlBeanDefinitionReader

我们先来看一段代码

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

applicationContext.xml文件内容如下:



    

这段代码非常简单也非常经典,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 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
            //元数据使用如下:
            //
            //    
            //
            //元数据的解析
            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源码解析之XmlBeanDefinitionReader)