Spring4.3.x 浅析xml配置的解析过程(1)——使用XmlBeanDefinitionReader解析xml配置

概述


Spring默认的BeanFactory是DefaultListableBeanFactory类,spring创建DefaultListableBeanFactory对象后,会把配置信息转换成一个一个的BeanDefinition对象,并把这些BeanDefinition对象注册到DefaultListableBeanFactory对象中,以供bean工厂创建bean实例。BeanDefinition对象存储的是单个bean的配置信息,比如依赖类、scope、是否延迟加载等等。

Spring可以通过4种方式配置bean,其一是基于xml的配置,其二种是基于xml+注解的配置,其三是基于java+注解的配置,其四是基于property文件的配置。前两种的配置信息使用XmlBeanDefinitionReader对象来解析;第三种的配置信息使用AnnotatedBeanDefinitionReader对象来解析;最后一种的配置信息使用PropertiesBeanDefinitionReader对象来解析。

而这里要讨论的主题是xml配置的解析过程,因此我们的战场在XmlBeanDefinitionReader,首先看看它的继承结构,如下图。

这里写图片描述

简单的说XmlBeanDefinitionReader实现了BeanDefinitionReader接口,BeanDefinitionReader的设计用意是加载BeanDefintion对象,下面是它加载BeanDefintion的4个接口方法。

    /**
     * 从单个指定的资源对象中加载BeanDefintion,并返回加载的BeanDefintion个数
     */
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

    /**
     * 从多个指定的资源对象中加载BeanDefintion,并返回加载的BeanDefintion个数
     */
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

    /**
     * 从单个指定的资源地址中加载BeanDefintion,并返回加载的BeanDefintion个数。
     * 如果资源加载器是ResourcePatternResolver对象,那么location参数可以使用通配符。
     */
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

    /**
     * 从多个指定的资源地址中加载BeanDefintion,并返回加载的BeanDefintion个数。
     */
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

这4个接口在后面会一一谈到,这里还有个问题留给大家,既然是加载BeanDefintion,为什么不是返回所有加载的BeanDefintion对象,而是返回加载的个数?

在spring中直接使用XmlBeanDefinitionReader的容器有XmlWebApplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext。下面就从XmlWebApplicationContext为例来探索xml配置的解析过程。

XmlWebApplicationContext解析配置文件分为以下2个过程:

1) 创建并初始化XmlBeanDefinitionReader 对象。

2)使用XmlBeanDefinitionReader 对象提供的接口方法来加载BeanDefinition对象。

下面我们通过spring源码来探讨这个2个过程。

1 创建并初始化XmlBeanDefinitionReader 对象


XmlWebApplicationContext在创建完BeanFacotry的时候会调用它的loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法来加载BeanDefinition,loadBeanDefinitions方法的代码如下。

@Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 根据给定的bean工厂创建新的XmlBeanDefinitionReader对象
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // 使XmlBeanDefinitionReader对象与上下文对象在同一个资源环境中
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        // 使用上下文对象为XmlBeanDefinitionReader对象的资源加载器
        beanDefinitionReader.setResourceLoader(this);
        // 设置EntityResolver对象,用于加载XML的xsd或者dtd文件
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // 钩子方法,允许子类在加载bean definition之前进一步设置XmlBeanDefinitionReader
        // ->比如,更改XmlBeanDefinitionReader自己提供的DocumentLoader
        // 或者BeanDefinitionDocumentReader等默认对象
        initBeanDefinitionReader(beanDefinitionReader);

        // 使用BeanDefinitionReader加载所有的BeanDefinition对象,见下面的代码
        loadBeanDefinitions(beanDefinitionReader);
    }

这段代码是为使用BeanDefinitionReader对象加载BeanDefinitioin对象做准备工作。BeanDefinitionRegistry对象是BeanDefinition对象的注册表,并且它是XmlBeanDefinitionReader对象创建时必须提供的,除此之外,其它的都使用默认或者由使用者提供。在XmlWebApplicationContext容器里,容器向BeanDefinitionReader提供了3个对象,第一个是资源环境Environment对象,它可用于判断beans标签的profile属性,后面会谈到;第二个是资源加载器ResourceLoader对象;最后一个是用于加载XML验证文件(dtd或者xsd文件)的EntityResolver对象。如果这些还不够,则可以扩展XmlWebApplicationContext容器,并重写initBeanDefinitionReader方法对BeanDefinitionReader做更多的初始化。

2 使用XmlBeanDefinitionReader 对象来加载BeanDefinition


首先,还是先预览一下XmlBeanDefinitionReader 加载BeanDefinition的流程:
这里写图片描述

上图只是大致描述了XmlBeanDefinitionReader对象加载BeanDefinition的过程,还有些重点的旁枝末节没有展现出来,但这并不影响我们了解XmlBeanDefinitionReader解析xml配置的过程。下面我们看看XmlWebApplicationContext如何使用XmlBeanDefinitionReader对象,以及XmlBeanDefinitionReader对象如何按照这个流程执行的。

创建完成BeanDefinitionReader对象后,XmlWebApplicationContext调用它的loadBeanDefinitions(XmlBeanDefinitionReader reader)方法来使用BeanDefinitionReader对象。下面是loadBeanDefinitions(XmlBeanDefinitionReader reader)方法的代码。

    /**
    * 使用XmlBeanDefinitionReader加载所有的BeanDefinition对象
    **/
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        // 获取容器需要加载的配置文件的地址
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                // 使用XmlBeanDefinitionReader一个一个的读取spring配置文件
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }

这部分代码是XmlBeanDefinitionReader 使用xml配置文件地址加载BeanDefinition对象的入口。这里所调用XmlBeanDefinitionReader的loadBeanDefinitions方法继承自XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader。下面代码是loadBeanDefinitions方法在AbstractBeanDefinitionReader类中的实现。

    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            // 使用资源模式解析器解析配置文件的路径并加载资源
            try {
                // 加载所有与指定location参数匹配的所有资源
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                // 加载指定的资源中的所有BeanDefinition
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            } catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        } else {
            // 直接加载资源且只加载一个资源,默认使用DefaultResourceLoader的getResource方法
            Resource resource = resourceLoader.getResource(location);
            // 加载指定的resource中的所有BeanDefinition
            int loadCount = loadBeanDefinitions(resource);

            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }   

这段代码主要做的事情是,使用BeanDefinitionReader对象所持有的ResourceLoader来生成Resource对象。然后调用BeanDefinitionReader的loadBeanDefinitions(Resource… resources)或者loadBeanDefinitions(Resource resource)方法来执行加载BeanDefinition的代码,其中,前一个方法通过多个资源文件来加载,后一个方法通过一个资源文件来加载。

首先我们从处理多个Resource对象的loadBeanDefinitions(Resource… resources)方法开始,这个方法已经在AbstractBeanDefinitionReader中有实现,并且XmlBeanDefinitionReader直接继承了它,代码如下。

    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            // 一个一个资源文件的加载BeanDefinition
            // 此接口方法在AbstractBeanDefinitionReader没有实现
            // XmlWebApplicationContext使用XmlBeanDefinitionReader
            // ->则调用XmlBeanDefinitionReader的实现
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }

这部分代码的所做的事情是遍历传入的资源Resource对象,并调用loadBeanDefinitions(Resource resource)方法加载每一个资源。这个方法在AbstractBeanDefinitionReader并没有实现,下面是此方法在XmlBeanDefinitionReader类中的实现代码。

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            // 读取资源文件输入流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // 根据指定的XML文件加载BeanDefinition
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

loadBeanDefinitions(EncodedResource encodedResource)方法主要做的事情是从Resource对象中获取xml文件输入流,并用它来创建InputSource对象。然后调用XmlBeanDefinitionReader的doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法,代码如下。

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            // 加载Document对象
            Document doc = doLoadDocument(inputSource, resource);
            // 注册BeanDefinition
            return registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException ex) {
            throw ex;
        } catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        } catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        } catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        } catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }
    /**
    * 获取Document对象
    **/
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        // 使用DocumentLoader来加载Document对象
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
    }

    /**
    * 获得XML验证模式,默认使用xsd
    */
    protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }

        return VALIDATION_XSD;
    }

这部分代码所做的事情首先是获取xml文档的验证模式,spring使用的xsd模式;然后调用DocumentLoader的loadDocument来读取InputSource对象中的XML内容并创建Document对象,XmlBeanDefinitionReader默认的DocumentLoader为DefaultDocumentLoader;最后调用registerBeanDefinitions(Document doc, Resource resource)方法来处理刚创建的Document对象,下面是XmlBeanDefinitionReader类中registerBeanDefinitions方法的代码。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 创建BeanDefinitionDocumentReader对象
        // 默认为DefaultBeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // documentReader需要持有当前的环境对象
        documentReader.setEnvironment(this.getEnvironment());
        int countBefore = getRegistry().getBeanDefinitionCount();

        //首先创建XmlReaderContext对象
        // 通过BeanDefinitionDocumentReader注册BeanDefinition
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

从这部分代码可以看出,XmlBeanDefinitionReader使用BeanDefinitionDocumentReader 对象来加载Document对象中的配置信息。

这部分代码主要做的事情有3步。第一步是创建BeanDefinitionDocumentReader对象,默认是DefaultBeanDefinitionDocumentReader;第二步是创建调用它的registerBeanDefinitions方法所需要的XmlReaderContext上下文对象,XmlReaderContext对象持有当前要读取的资源、xml命名空间处理;第三步是调用documentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)处理Document对象。下面分别探讨这三步的代码。

第一步创建BeanDefinitionDocumentReader对象。

    /**
    * 创建BeanDefinitionDocumentReader对象
    **/
    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }

第二步:创建XmlReaderContext上下文对象。


    /**
    * 创建XmlReaderContext对象
    **/
    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }

    /**
    * 创建NamespaceHandlerResolver对象
    **/
    public NamespaceHandlerResolver getNamespaceHandlerResolver() {
        if (this.namespaceHandlerResolver == null) {
            this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
        }
        return this.namespaceHandlerResolver;
    }

    /**
    * 创建默认的命名空间处理器容器
    **/
    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
    }

第三步执行DefaultBeanDefinitionDocumentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法。

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        // 获得root节点
        Element root = doc.getDocumentElement();
        // 注册root节点中的所有BeanDefinition
        doRegisterBeanDefinitions(root);
    }

这一步是处理Document对象的重点也是入口。XML文件中只允许有一个根节点,上面的代码所做的事情就是保存XmlReaderContext 对象并提取根节点,然后调用DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(Element root)方法,这个方法的代码如下。

    protected void doRegisterBeanDefinitions(Element root) {

        // 在这个方法中,递归所有嵌套<beans>元素。
        // 为了正确地延用并保存<beans>元素的default-*属性
        // ->需要把父节点<beans>的delegate记录下来,也许这个delegate可能为null。
        BeanDefinitionParserDelegate parent = this.delegate;
        // 为当前的<beans>节点创建delgate对象
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            // 检查beans标签上的profile属性
            // 声明:public static final String PROFILE_ATTRIBUTE = "profile";
            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)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        preProcessXml(root);
        // 解析根节点
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        // 解析完嵌套的<beans>标签后,还原父节点的delegate
        this.delegate = parent;
    }

这段代码主要也分成3步,第一步创建BeanDefinitionParserDelegate对象,这个对象的作用是代理DefaultBeanDefinitionDocumentReader解析BeanDefinition对象;第二步检查bean标签的profile属性值是否与环境的匹配,如果不匹配则不处理而直接返回;第三步执行parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法。下面分别探讨第一步和第三步。

(1) 创建BeanDefinitionParserDelegate对象,代码如下

    protected BeanDefinitionParserDelegate createDelegate(
            XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {

        // 创建BeanDefinitionParserDelegate 
        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);

        // 初始化delegate的Defaults
        // 如果当前<beans>节点的属性值等于默认值,则使用父节点<beans>对应的属性值。
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }

(2) 执行DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法,代码如下

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 检查root节点的命名空间是否为默认命名空间
        // spring配置文件中默认的命名空间为"http://www.springframework.org/schema/beans"
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            // 遍历root节点下的所有子节点
            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);
        }
    }

这段代码主要是区分节点的命名空间,根据不同命名空间,调用相应的方法。如果节点在默认命名空间(http://www.springframework.org/schema/beans),则调用DefaultBeanDefinitionDocumentReader的parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)方法,否则调用BeanDefinitionParserDelegate 的parseCustomElement(Element ele)方法。

(1)处理自定义命名空间下的节点。执行BeanDefinitionParserDelegate的 parseCustomElement(Element ele) 方法,代码如下

    public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }
    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));
    }

这部分代码主要是获取自定义命名空间的处理器,然后执行处理器的parse方法来创建一个BeanDefinition对象。

(2)处理默认命名空间下的节点。 执行DefaultBeanDefinitionDocumentReader的parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)方法,代码如下。

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            // 处理import节点元素
            // 这里同样是获取配置文件并把注册BeanDefinition对象
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            // 处理alias节点元素
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            // 处理bean节点元素
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 处理beans节点元素,递归调用doRegisterBeanDefinitions,详见4.3
            doRegisterBeanDefinitions(ele);
        }
    }

这段代码是处理import、beans、alias、bean标签的入口方法。

  • import标签是引入其它spring配置文件;
  • beans标签是对bean进行分类配置,比如用一个beans来管理测试环境的bean,用另一个beans来管理生产环境的bean;
  • alias标签是为一个已定义了的bean取别名,它的name属性值是bean的id,alias属性值是要取的别名,多个别名用英文逗号、分号或者空格隔开;
  • bean标签的信息就是spring要实例化的对象。

不管是什么标签,只有利用bean标签才会生成BeanDefinition对象,下面重点探讨的bean标签处理。 执行DefaultBeanDefinitionDocumentReader的processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法,代码如下。

    /**
    * 解析bean节点,并注册BeanDefinition对象
    */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

        // 创建BeanDefinitionHolder
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 装饰BeanDefinition
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 注册已经创建好的BeanDefintion
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // 发送BeanDefinition注册事件
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

这段代码分成三步。第一步,根据传入的Element对象(bean标签的)调用代理对象的parseBeanDefinitionElement(Element ele)方法创建BeanDefinitionHolder 对象,这个对象持有创建好的BeanDefinition对象、bean的id和bean的别名。

第二步,调用代理对象的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder)来对BeanDefinition对象再加工,主要是解析bean标签中自定义属性和自定义标签。

第三步,调用工具类BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,这个方法用于注册创建好的BeanDefinition。

前面两步主要是完成BeanDefintion的创建和自定义,在这里不做更深入的探讨,下面我们看看第三步,注册BeanDefinition对象。 执行BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,把BeanDefinition对象注册到BeanDefinitionRegistry 对象中

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        String beanName = definitionHolder.getBeanName();
        // 把BeanDefinition对象注册到BeanDefinitionRegistry 对象中
        // 我们通过DefaultListableBeanFactory为例,介绍BeanDefinition的注册
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 把bean id与别名关联起来
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String aliase : aliases) {
                registry.registerAlias(beanName, aliase);
            }
        }
    }

这部分代码的作用是调用BeanDefinitionRegistry 的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法来注册BeanDefinition,然后调用它的registerAlias(String name, String alias)来使bean的id和别名关联起来。

DefaultListableBeanFactory实现了BeanDefinitionRegistry 接口的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法,代码如下。


    //---------------------------------------------------------------------
    // Implementation of BeanDefinitionRegistry interface
    //---------------------------------------------------------------------

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            } catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 覆盖原来的BeanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            if (hasBeanCreationStarted()) {
                // BeanDefintion已经注册完成,而其他bean正在创建
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            } else {
                // 注册BeanDefintion还未完成
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            // 重置BeanDefintiion
            resetBeanDefinition(beanName);
        }
    }

这段代码的主要作用是DefaultListableBeanFactory把创建完成的BeanDefinition保存到Map对象beanDefinitionMap中,同时还要把以前已经创建好的bean(如果存在)销毁掉。

总结


1. XmlBeanDefinitionReader的真正身份

XmlBeanDefinitionReader并不是xml配置的真正解析者,它只是相当于一个指挥官。当它收到一条需要加载BeanDefinition对象的任务后,它只会协调手下去完成相应的工作,它的手下有:

  • ResourceLoader,它把指定的配置文件地址封装成Resource对象。

  • DocumentLoader,它把Resource对象中的XML文件内容转换为Document对象。默认使用DocumentLoader的实现类DefaultDocumentLoader来加载Document对象。

  • BeanDefinitionDocumentReader,它把Document对象中包含的配置信息转换成BeanDefinition对象并把它注册到BeanDefintionRegistry对象中。默认使用DefaultBeanDefinitionDocumentReader来操作Document对象。在DefaultBeanDefinitionDocumentReader的实现中,它的责任是遍历xml根节点下的子节点,并把处理bean标签和自定义命名空间的标签(比如aop:,context:,p:等)的细节委托给BeanDefinitionParserDelegate对象,BeanDefinitionParserDelegate才是真正解析配置文件的地方。

  • NamespaceHandlerResolver,用于获取非默认命名空间的处理器,默认是DefaultNamespaceHandlerResolver对象。它虽然由XmlBeanDefinitionReader提供,但真正的使用者是BeanDefinitionParserDelegate类。

2. 更改XmlBeanDefinitionReader的默认对象

比如需要更改DocumentLoader、NamespaceHandlerResolver或者BeanDefinitionDocumentReader,甚至是ResourceLoader。

继承XmlWebApplicationContext,并重写initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader),这个方法在XmlWebApplicationContext只是一个空实现,特意留给子类在使用XmlBeanDefinitionReader对象加载BeanDefinition之前对这个对象进行定制的。

遗留问题

这一篇文章只是大致的过了一下XmlBeanDefinitionReader如何根据指定的配置文件地址来加载并注册BeanDefintion对象,至于以下细节并没有过多的描述。

  1. ResourceLoader如何根据指定的location生成Resource对象。

  2. DocumentLoader如何根据xml输入流生成为Document对象。

  3. Spring如何解析bean标签的属性以及子节点。

  4. Spring如何解析非默认命名空间的配置。

  5. Spring如何解析注解配置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章