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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

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

    /** * 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图:
20191017100171\_1.png
从上面图中可以看出XmlBeanDefinitionReader实现了BeanDefinitionReader接口,该接口从名称上可以看出主要用来读取BeanDefinition对象,接口提供了以下方法:
20191017100171\_2.png

  • 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如何知道使用哪个类进行处理呢?具体分析请看下一篇


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring源码学习之三 XmlWebApplicationContext.loadBeanDefinitions源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏