beanfactory篇-(十二)bean描述信息的封装抽象过程-九零后大叔的技术博客

沙海 2021年4月24日09:20:36Java评论35字数 8197阅读27分19秒阅读模式
摘要

回顾:通过前面的文章,我们基本上知道了spring的核心代码的整体脉络,一块是XmlBeanFacotry对象的创建,也就是spring容器的启动过程。但是中间的applicationContext.xml的解析过程,没有重点跟踪,本章节我们将重点跟踪一下applicationContext.xml的解析过程。

文章源自JAVA秀-https://www.javaxiu.com/19905.html

回顾:通过前面的文章,我们基本上知道了spring的核心代码的整体脉络,一块是XmlBeanFacotry对象的创建,也就是spring容器的启动过程。但是中间的applicationContext.xml的解析过程,没有重点跟踪,本章节我们将重点跟踪一下applicationContext.xml的解析过程。文章源自JAVA秀-https://www.javaxiu.com/19905.html

文章源自JAVA秀-https://www.javaxiu.com/19905.html

注意:看下面的代码的过程,细心的读者会发现好像这个过程和前一节的文章很类似啊,但是我们这里关注的重点是applicationContext.xml文件资源的一路演变过程。applicationContext.xml文件是我们整个spring容器未启动时候的BeanDefinition对象的最初的信息承载者,而spring容器启动的过程就是要把applicationContext.xml文件的若干Bean描述信息,一步一步转换成为若干BeanDefinition对象,然后存放到beanDefinitionMap中。文章源自JAVA秀-https://www.javaxiu.com/19905.html

1. 查看applicationContext.xml这个资源的变化流程

//主干代码
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

new ClassPathResource("applicationContext.xml")

public ClassPathResource(String path) {
    this(path, (ClassLoader) null);
}

//我们先看看第一次演变-经过new ClassPathResource的封装,这个时候的applicationContext.xml文件资源由"applicationContext.xml"字符串变成了ClassPathResource对象,此时的文件任然没有读取。
public ClassPathResource(String path, ClassLoader classLoader) {
    Assert.notNull(path, "Path must not be null");
    String pathToUse = StringUtils.cleanPath(path);
    if (pathToUse.startsWith("/")) {
        pathToUse = pathToUse.substring(1);
    }
    this.path = pathToUse;
    this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}


public XmlBeanFactory(Resource resource) throws BeansException {
    //回到主干代码
    this(resource, null);
}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    super(parentBeanFactory);
    //进一步深入主干代码
    this.reader.loadBeanDefinitions(resource);
}

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    //第二次演变-此时的applicationContext.xml文件资源由ClassPathResource对象变成了EncodedResource对象了。
    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 {
        //第三次演变-此时的applicationContext.xml文件资源由EncodedResource对象变成InputStream对象了。
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            //第四次演变-此时的applicationContext.xml文件资源由InputStream对象变成InputSource对象了。
            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();
        }
    }
}

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        //第五次演变-此时的applicationContext.xml文件资源InputStream对象变成Document对象
        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);
    }
}

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;
}

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    //第六次演变-此时的applicationContext.xml文件资源由Document对象变成了Element对象
    Element root = doc.getDocumentElement();
    //主干代码
    doRegisterBeanDefinitions(root);
}

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 void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        //第七次演变-此时的applicationContext.xml文件资源由Element对象变成了一个NodeList对象(或者说是一堆Node对象)
        NodeList nl = root.getChildNodes();
        //此时的Node对象可以理解为就是对默认标签(import,alias,bean,beans)或者自定义标签(mvc,context,aop,transaction,dubbo)信息的封装,而下面的循环时将所有的Node信息解析成为BeanDefinition对象放置到一个Map中去,前面的章节已经提到了这里不细说。
        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);
    }
}

抽取applicationContext.xml文件的整个演变成为BeanDefinition对象的关键步骤文章源自JAVA秀-https://www.javaxiu.com/19905.html

//第一次演变-经过new ClassPathResource的封装,这个时候的applicationContext.xml文件资源由"applicationContext.xml"字符串变成了ClassPathResource对象。
new ClassPathResource("applicationContext.xml")
//第二次演变-此时的applicationContext.xml文件资源由ClassPathResource对象变成了EncodedResource对象了。
new EncodedResource(resource)
//第三次演变-此时的applicationContext.xml文件资源由EncodedResource对象变成InputStream对象了。
InputStream inputStream = encodedResource.getResource().getInputStream();
//第四次演变-此时的applicationContext.xml文件资源由InputStream对象变成InputSource对象了。
InputSource inputSource = new InputSource(inputStream);
//第五次演变-此时的applicationContext.xml文件资源InputStream对象变成Document对象
Document doc = doLoadDocument(inputSource, resource);
//第六次演变-此时的applicationContext.xml文件资源由Document对象变成了Element对象
Element root = doc.getDocumentElement();
//第七次演变-此时的applicationContext.xml文件资源由Element对象变成了一个NodeList对象(或者说是一堆Node对象)
NodeList nl = root.getChildNodes(); 

启动一个spring容器所需的所有的bean描述信息的承载者,从”applicationContext.xml”字符串,到ClassPathResource对象,一步一步到一堆Node对象,最后变成一批BeanDefinition对象,放置到了beanDefinitionMap对象中去。通过上面的这些步骤,我们可以看到我们的applicationContext.xml文件资源,由非常抽象的一个”applicationContext.xml”字符串,一步一步的被解析,从而变的越来越具象,越来越具体了。文章源自JAVA秀-https://www.javaxiu.com/19905.html

2. applicationContext文件资源通过输入流读取

通过前面的一些分析,我们大致了解了applicationContext.xml资源是如何被一步一步转换成为BeanDefinition对象的。但是好像有一个步骤我们丢失了,就是applicationContext文件资源到底是如何被读取的?答案在第二次演变InputStream inputStream = encodedResource.getResource().getInputStream();的getInputStream()方法,我们看看下面的方法便知道了。文章源自JAVA秀-https://www.javaxiu.com/19905.html

@Override
public InputStream getInputStream() throws IOException {
    InputStream is;
    if (this.clazz != null) {
        is = this.clazz.getResourceAsStream(this.path);
    }
    else if (this.classLoader != null) {
        //在这里applicationContext.xml文件资源被转换成了InputStream流对象
        is = this.classLoader.getResourceAsStream(this.path);
    }
    else {
        is = ClassLoader.getSystemResourceAsStream(this.path);
    }
    if (is == null) {
        throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
    }
    return is;
}

beanfactory篇-(十二)bean描述信息的封装抽象过程-九零后大叔的技术博客 文章源自JAVA秀-https://www.javaxiu.com/19905.html

更多知识请关注公众号文章源自JAVA秀-https://www.javaxiu.com/19905.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定