spring源码学习之三 XmlWebApplicationContext.loadBeanDefinitions源码分析

https://blog.csdn.net/wenjiangchun/article/details/50619568

https://blog.csdn.net/wenjiangchun/article/details/50622475

https://blog.csdn.net/wenjiangchun/article/details/50628728

https://blog.csdn.net/wenjiangchun/article/details/50629764

https://blog.csdn.net/wenjiangchun/article/details/50669561

https://blog.csdn.net/wenjiangchun/article/details/54345207

接上篇 
首先查看该方法源码:

/**
     * Loads the bean definitions via an XmlBeanDefinitionReader.
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     * @see #initBeanDefinitionReader
     * @see #loadBeanDefinitions
     */
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(getEnvironment());
        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);
    }

    /**
     * Initialize the bean definition reader used for loading the bean
     * definitions of this context. Default implementation is empty.
     * <p>Can be overridden in subclasses, e.g. for turning off XML validation
     * or using a different XmlBeanDefinitionParser implementation.
     * @param beanDefinitionReader the bean definition reader used by this context
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
     */
    protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
    }

    /**
     * Load the bean definitions with the given XmlBeanDefinitionReader.
     * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
     * therefore this method is just supposed to load and/or register bean definitions.
     * <p>Delegates to a ResourcePatternResolver for resolving location patterns
     * into Resource instances.
     * @throws IOException if the required XML document isn't found
     * @see #refreshBeanFactory
     * @see #getConfigLocations
     * @see #getResources
     * @see #getResourcePatternResolver
     */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }


从源码及方法注释可以看出,loadBeanDefinitions方法首先创建一个XmlBeanDefinitionReader对象,然后将当前applicationContext中创建的DefaultListableBeanFactory以及Environment等对象传递给XmlBeanDefinitionReader对象,最后调用XmlBeanDefinitionReader.loadBeanDefinitions(configLocation)来完成实际BeanDefinition加载工作,那么在XmlBeanDefinitionReader.loadBeanDefinitions(configLocation)内部又是怎么一个流程?接着进入XmlBeanDefinitionReader类中的loadBeanDefinitions(configLocation)来查看源码实现: 
首先上XmlBeanDefinitionReader类UML图: 
 
从上面图中可以看出XmlBeanDefinitionReader实现了BeanDefinitionReader接口,该接口从名称上可以看出主要用来读取BeanDefinition对象,接口提供了以下方法: 


BeanDefinitionRegistry getRegistry();获取BeanDefinition注册容器,以便将加载进来的beanDefinition存放到该容器中.查看源代码可以知道在XmlBeanDefinitionReader中,该方法返回的其实就是在XmlWebApplicationContext中创建的DefaultListableBeanFactory对象.
ResourceLoader getResourceLoader();返回解析资源加载器对象,该方法实现是在BeanDefinitionReader抽象实现类AbstractBeanDefinitionReader中来实现,查看源代码可以看出其实是通过this.resourceLoader = new PathMatchingResourcePatternResolver();返回一个PathMatchingResourcePatternResolver对象.
loadBeanDefinition提供了几个方法重载,在这几个方法中除了loadBeanDefinitions(Resource resource)是在XmlBeanDefinitionReader中实现外,其它3个是在AbstractBeanDefinitionReader中实现,其实这3个最终调用的是loadBeanDefinitions(Resource resource),只不过loadBeanDefinitions(String location)首先将location解析成对应Resource对象,而用来解析的功能即使用getResourceLoader()返回的ResourceLoader对象来完成(在AbstractBeanDefinitionReader中即为PathMatchingResourcePatternResolver).
loadBeanDefinitions(Resource resource)方法是在XmlBeanDefinitionReader类中来实现的,源码如下:
XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)

/**
     * Load bean definitions from the specified XML file.
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    /**
     * Load bean definitions from the specified XML file.
     * @param encodedResource the resource descriptor for the XML file,
     * allowing to specify an encoding to use for parsing the file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    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());
                }
                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();
            }
        }
    }
/**
     * Actually load bean definitions from the specified XML file.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     * @see #doLoadDocument
     * @see #registerBeanDefinitions
     */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);
            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);
        }
    }

    /**
     * Actually load the specified document using the configured DocumentLoader.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the DOM Document
     * @throws Exception when thrown from the DocumentLoader
     * @see #setDocumentLoader
     * @see DocumentLoader#loadDocument
     */
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }
/**
     * Register the bean definitions contained in the given DOM document.
     * Called by {@code loadBeanDefinitions}.
     * <p>Creates a new instance of the parser class and invokes
     * {@code registerBeanDefinitions} on it.
     * @param doc the DOM document
     * @param resource the resource descriptor (for context information)
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of parsing errors
     * @see #loadBeanDefinitions
     * @see #setDocumentReaderClass
     * @see BeanDefinitionDocumentReader#registerBeanDefinitions
     */
    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;
    }

    /**
     * Create the {@link BeanDefinitionDocumentReader} to use for actually
     * reading bean definitions from an XML document.
     * <p>The default implementation instantiates the specified "documentReaderClass".
     * @see #setDocumentReaderClass
     */
    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }

    /**
     * Create the {@link XmlReaderContext} to pass over to the document reader.
     */
    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }
/**
     * Lazily create a default NamespaceHandlerResolver, if not set before.
     * @see #createDefaultNamespaceHandlerResolver()
     */
    public NamespaceHandlerResolver getNamespaceHandlerResolver() {
        if (this.namespaceHandlerResolver == null) {
            this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
        }
        return this.namespaceHandlerResolver;
    }

    /**
     * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
     * Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
     */
    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
    }


从上面源码中可以看出loadBeanDefinitions处理过程中将Resource解析成Document文档,然后具体解析Document文档的过程交由BeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法来完成 
注意XmlReaderContext对象的创建.在上面createReaderContext方法中,将this对象传递给新创建的XmlReaderContext,因为XmlReaderContext内部需要一个BeanDefinitionRegistry和BeanDefinition关联,而通过this可以获取到关联的BeanDefinitionRegistry. 
NamespaceHandlerResolver将在下篇分析

BeanDefinitionDocumentReader
该接口只有registerBeanDefinitions(Document doc, XmlReaderContext readerContext)一个方法,用来读取Document文档并使用XmlReaderContext来处理Document,注意XmlReaderContext绑定了一个XmlBeanDefinitionReader对象,该对象又关联BeanDefinitionRegistry,所以才能将处理后的BeanDefinition和BeanDefinitionRegistry关联起来.

DefaultBeanDefinitionDocumentReader
该类实现了BeanDefinitionDocumentReader接口,用来将Document文档解析成BeanDefinition对象,下面为该类部分源码:

public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;

    public static final String NESTED_BEANS_ELEMENT = "beans";

    public static final String ALIAS_ELEMENT = "alias";

    public static final String NAME_ATTRIBUTE = "name";

    public static final String ALIAS_ATTRIBUTE = "alias";

    public static final String IMPORT_ELEMENT = "import";

    public static final String RESOURCE_ATTRIBUTE = "resource";

    public static final String PROFILE_ATTRIBUTE = "profile";
    private XmlReaderContext readerContext;

    private BeanDefinitionParserDelegate delegate;

/**
     * This implementation parses bean definitions according to the "spring-beans" XSD
     * (or DTD, historically).
     * <p>Opens a DOM Document; then initializes the default settings
     * specified at the {@code <beans/>} level; then parses the contained bean definitions.
     */
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
}
/**
     * Register each bean definition within the given root {@code <beans/>} element.
     */
    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;
    }

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

        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }

    /**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     */
    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);
        }
    }

    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)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }


类中定义了对应xml文档中的常量值如”import”,”beans”等,这些常量对应spring配置文件元素名称.如<import>元素
定义了XmlReaderContext和BeanDefinitionParserDelegate两个类型变量,XmlReaderContext前面提到过为xml读取上下文,那么BeanDefinitonParserDelegate呢?从名称上可以看出此类是一个委托类,主要用来处理bean元素,DefaultBeanDefinitionDocumentReader在处理xml元素节点时,如果节点元素对应的namespace不为”http://www.springframework.org/schema/beans“命名空间时,此时将元素委托给BeanDefinitonParserDelegate处理,如<context:component-scan>元素对应的namespace为”http://www.springframework.org/schema/context“,当处理到该元素的时候则委托给BeanDefinitonParserDelegate类处理.该部分功能在parseBeanDefinitions方法实现.
parseDefaultElement方法主要用来处理xml节点并且对应namespace为”http://www.springframework.org/schema/beans“的元素,如<import>,在实际应用中会在一个spring配置文件中import多个配置文件,此时在解析<import>元素时,会调用parseDefaultElement根据元素名称调用对应方法,如<import>会调用importBeanDefinitionResource方法进行处理,具体实现参考源码.

总结
从上面可以看出在XmlWebApplicationContext.loadBeanDefinitions方法中,首先创建一个XmlBeanDefinitionReader对象,将XmlWebApplicationContext创建的内置容器和配置文件所在位置信息传递给该对象,然后由该对象去执行读取配置加载工作.而XmlBeanDefinitionReader首先根据传递过来的配置文件路径解析成具体实际路径,然后将配置文件读取成Document,并生成一个XmlReaderContext对象后交由DefaultBeanDefinitionDocumentReader对象处理,DefaultBeanDefinitionDocumentReader在拿到Document后,只处理namespace为”http://www.springframework.org/schema/beans“下的元素,如"<bean>","<import>"等,对于其它命名空间下的元素则委托给BeanDefinitonParserDelegate类的parseCustomElement(Element ele)方法进行处理(其实该命名空间下元素具体处理过程也是委托给BeanDefinitonParserDelegate类中的方法进行处理,只不过在DefaultBeanDefinitionDocumentReader.parseDefaultElement方法中分别进行判断来调用BeanDefinitonParserDelegate的哪个方法),那么对于元素Spring如何知道使用哪个类进行处理呢?具体分析请看下一篇
--------------------- 
作者:wenjiangchun 
来源:CSDN 
原文:https://blog.csdn.net/wenjiangchun/article/details/50628728 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello_world!

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值