Spring源码学习【二】IOC容器的初始化(二)BeanDefinition载入

目录

一、总览

二、源码分析

(一)获取Document

(二)解析Document


一、总览

经过前一篇(Spring源码学习【二】IOC容器的初始化(一)Resource定位)的分析,我们对IOC容器的初始化过程已经有了一定的了解。IOC的初始化由refresh()方法启动,最终对Resource的载入是由XmlBeanDefinitionReader处理的。参考上一节中6.loadBeanDefinitions的分析,AbstractBeanDefinitionReader中多处调用了loadBeanDefinitions(Resource)方法,这是一个模板方法,实际由其子类XmlBeanDefinitionReader实现,并完成BeanDefinition载入的过程。

BeanDefinition的载入可以分为两个过程:首先,通过documentLoader获取得到Document对象;然后,根据Spring Bean的规则解析Document得到BeanDefinition,下面从源码的角度对这个过程进行分析。

二、源码分析

(一)获取Document

首先, XmlBeanDefinitionReader中重写了父类的loadBeanDefinitions(Resource)方法,这个方法就是加载BeanDefinition的入口,在这个方法中,将Resource封装成带有编码信息的EncodedResource对象。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    ……
    /**
     * 根据指定的xml资源文件加载BeanDefinition
     */
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        // 这里会为Resource添加编码等信息封装成一个EncodedResource对象
        return loadBeanDefinitions(new EncodedResource(resource));
    }
}

然后,在loadBeanDefinitions(EncodedResource)中处理了Resource的循环加载,并将Resource封装成代表XML实体的InputSource(这里对LocalThread和循环加载存在疑问)。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    ……
    /**
     * 根据指定的xml资源文件加载BeanDefinition,允许指定解析文件使用的编码方式
     */
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreEx-ception {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }
        // private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
        //	new NamedThreadLocal<>("XML bean definition resources currently being loaded");
        // 这里使用的localThread会为每个线程创建一个Set<EncodedResource>副本,保证线程之间的数据访问不会出现冲突
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        // 我们在配置文件中可以通过<import>标签引用其他资源文件,当出现资源文件之间的循环引用时会抛出异常
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            // Resource是封装了一系列I/O操作的资源,这里拿到输入流InputStream
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                // 将InputStream封装成一个InputSource,InputSource是xml SAX解析的数据源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // 开始加载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();
            }
        }
    }
}

接下来,调用了 doLoadBeanDefinitions(InputSource, Resource)方法,真正开始从XML资源文件加载BeanDefinition。前面已经提到过,BeanDefinition的载入首先需要获取Document,这里有两个核心方法:其一,doLoadDocument;其二,registerBeanDefinitions。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    ……
    /**
     * 真正开始从XML资源文件加载BeanDefinition
     * 两个核心方法:doLoadDocument和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);
        }
    }

    /**
     * 通过DocumentLoader加载XML资源文件,以Document的形式返回,用于后续的XML解析。
     * 这里的Document是由w3c定义的一个接口,用于表示整个html或xml文件。
     */
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        // inputSource:SAX解析方式的输入数据
        // getEntityResolver:实体解析器,分为ResourceEntityResolver和DelegatingEntityResolver
        // ResourceEntityResolver通常是对applicationContext的包装
        // DelegatingEntityResolver是对dtdResolver和schemaResolver的包装,分别对应关于DTDs和SCHEMAs请参考http://wiki.jikexueyuan.com/project/xml/dtds.html
        // errorHandler SAX解析的错误错误处理
        // getValidationModeForResource:获取xml文件的验证方式(dtd或xsd方式)
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
    }

    /**
     * 根据给定的Resource资源获取XML验证方式
     * 如果没有明确指定验证方式,则会根据Resource内容进行判断
     */
    protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        // 读取Resource内容判断验证规则 
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        // Hmm, we didn't get a clear indication... Let's assume XSD,
        // since apparently no DTD declaration has been found up until
        // detection stopped (before finding the document's root tag).
        return VALIDATION_XSD;
    }

    /**
     * 检测在XML文件上执行的哪种验证
     * 如果内容包含DOCTYPE则为DTD验证,否则为XSD验证
     */
    protected int detectValidationMode(Resource resource) {
        if (resource.isOpen()) {
            throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: " + "cannot determine validation mode automatically. Either pass in a Resource " + "that is able to create fresh streams, or explicitly specify the validationMode " + "on your XmlBeanDef-initionReader instance.");
        }

        InputStream inputStream;
        try {
            inputStream = resource.getInputStream();
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException( "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + "Did you attempt to load directly from a SAX InputSource without specifying the " + "validationMode on your XmlBeanDefinitionReader instance?", ex);
		}

        try {
            // 进行检测
            return this.validationModeDetector.detectValidationMode(inputStream);
        }
        catch (IOException ex) { 
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex);
        }
    }

    /**
     * 注册DOM document中包含的BeanDefinitions
     */
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefini-tionStoreException {
        // 首先创建一个读取器,这里使用的是读取Spring默认XML格式(DTD和XSD格式)BeanDefinition的读取器
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 注册BeanDefinition,是一个模板方法,实际调用的是DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
}

到这里,已经完成了BeanDefinition载入的第一个过程:获取Document对象,用这个对象来表示XML资源文件,下面一步重要的操作就是对Document进行解析了。

(二)解析Document

经过以上的分析,我们已经知道,解析Document是在DefaultBeanDefinitionDocumentReader中完成的,下面让我们来看一看DefaultBeanDefinitionDocumentReader的部分代码:

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader{
    ……
    /**
     * 实现根据Spring bean规则解析BeanDefinition的功能
     */
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        // 获取到XML的根元素:<beans/>
        Element root = doc.getDocumentElement();
        // 解析根元素下的BeanDefinition
        doRegisterBeanDefinitions(root);
    }

    /**
     * 实现根据Spring bean规则解析BeanDefinition的功能
     */
    protected void doRegisterBeanDefinitions(Element root) {
        // <beans/> 嵌套时会递归调用该方法,首先记录下当前的委托对象为父对象,然后创建一个新的子委托对象,在本次调用中使用子对象进行处理,目的是为了初始化每个<beans>的default-*属性,比如 <beans de-fault-lazy-init="true" > 等
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        // profile环境处理
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDele-gate.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和postProcessXml都是模板方法,默认实现是空的,留给子类进行扩展,子类中可以处理一些自定义的bean,并把这些bean转换为标准的Spring BeanDefinition
        preProcessXml(root);
        // 解析<beans/>下的BeanDefinition
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

    /**
     * 解析root元素下的 import、alias、bean
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 检查root元素是否为http://www.springframework.org/schema/beans默认命名空间
        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;
                    // 处理beans默认命名空间下的元素标签,<import>、<alias>、<bean>、<beans>
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    // 处理非默认命名空间元素标签,如<context:component-scan>、<tx:annotation-driven>等
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate){
        // 解析<import>标签,从引用的资源中加载BeanDefinition
        // 这里的解析可能会抛出资源循环加载的异常
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // 解析<alias>,向bean工厂注册别名
        // 具体是在SimpleAliasRegistry这个类中,以HaspMap持有bean的别名和原名的关系的
        // 这里需要注意别名的循环引用
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // 解析<bean>,并向bean工厂注册bean
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 这里体现了<beans>嵌套的递归
            doRegisterBeanDefinitions(ele);
        }
    }

    /**
     * 解析<bean>并向bean工厂注册
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // 委托BeanDefinitionParserDelegate解析bean元素,这里的BeanDefinitionHolder是对BeanDefinition、BeanName、别名的封装,BeanDefinition中包含了id、class、name等属性。
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 解析元素下的自定义标签
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 向bean 工厂注册BeanDefinition和bean别名
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderCon-text().getRegistry());
            } 
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
            }
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
}

到这里,我们发现真正将Document元素解析为BeanDefinition的过程是委托给BeanDefinitionParserDelegate处理的,在这个类中我们需要关注两个重点:其一,解析bean元素;其二,解析自定义元素。代码如下:

public class BeanDefinitionParserDelegate {
    ……
    /**
     * 解析<bean>元素
     */
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }

    /**
     * 解析<bean>元素
     */
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // 获取元素的id和name属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		
        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }
        // 以id或别名作为bean的名称
        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
            }
        }
        // 检查bean名称和别名是否已经被注册
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        // 解析element元素,得到具体的BeanDefinition
        // 这里不会处理beanName和aliases
        // TODO:待深入学习
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, con-tainingBean);
        // 如果beanDefinition没有指定id和name,则为其生成beanName
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            // 最后,返回一个BeanDefinitionHolder对象,这个对象中持有beanDefinition、beanName、beanAliases
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }
        return null;
    }

    /**
     * 解析自定义元素
     */
    public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }

    /**
     * 解析自定义元素
     */
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        // 获取元素的命名空间
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        // 获取相应命名空间的Handler,各个命名空间都有对应的Handler,如ContextNamespaceHandler、AopNamespaceHandler等
        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));
    }
}

到这里,Document的解析就完成了,至于BeanDefinition具体的解析处理这里没有深入学习,后续会补上。

猜你喜欢

转载自blog.csdn.net/greedystar/article/details/81186930