Spring 源码第一天XmlBeanFactory

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-20 22:31:05

使用spring的第一行代码

BeanFactory beanFactoryf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

Spring入门使用的第一行代码
其中做的第一件事就是:new ClassPathResource(“beanFactory.xml”);创建了一个ClassPathResource
在这里插入图片描述
可以从层次中看到最上层为:InputStreamSource,Resource

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
    boolean exists();

    default boolean isReadable() {
        return this.exists();
    }

    default boolean isOpen() {
        return false;
    }

    default boolean isFile() {
        return false;
    }

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    default ReadableByteChannel readableChannel() throws IOException {
        return Channels.newChannel(this.getInputStream());
    }

    long contentLength() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String var1) throws IOException;

    @Nullable
    String getFilename();

    String getDescription();
}

这里解释一下Resource类,Spring对其内部使用到的资源实现了自己的抽象结构Resource。他的上一个接口InputStreamSource 能返回InputStream的类。其中可以独立使用其方法,便可得到其中相应的InputStream

Resource s = new ClassPathResource("beanFactory.xml")
InputStream is = s.getInputStream();

1.以上代码根据相应路径将配置文件封装成Spring内部资源结构Resource。

下面紧接着调用了XmlBeanFactory的构造函数。

 public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
        this.reader.loadBeanDefinitions(resource);
    }

构造函数接收一个Resource对象,或者再多一个BeanFactory(这里为空,用来factory合并super(parentBeanFactory),这里先不管他)。下一步创建reader:XmlBeanDefinitionReader,接下来this.reader.loadBeanDefinitions(resource);开始加载资源resource。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
    }

1.先对参数Resource进行EncodedResource封装。
EncodedResource:其中的主要逻辑体现在getReader()方法中,当设置了编码属性的时候Spring会使用相应的编码作为输入流的编码。

public EncodedResource(Resource resource) {
        this(resource, (String)null, (Charset)null);
    }
private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
        Assert.notNull(resource, "Resource must not be null");
        this.resource = resource;
        this.encoding = encoding;
        this.charset = charset;
    }

虽然在构造函数中没看到编码的体现,主要编码体现在getReader()中,整体封装成一个EncodedResource对象再传入loadBeanDefinitions函数中。

public Reader getReader() throws IOException {
        if (this.charset != null) {
            return new InputStreamReader(this.resource.getInputStream(), this.charset);
        } else {
            return this.encoding != null ? new InputStreamReader(this.resource.getInputStream(), this.encoding) : new InputStreamReader(this.resource.getInputStream());
        }
    }

以下代码中从A代码开始才是我们要去关注的地方,inputStream 获得最开始说的Spring内部抽象结构的Resource的父接口所定义的getInputStream();获得文件,最后看到一些文件上的转换,最终来到doLoadBeanDefinitions(inputSource, encodedResource.getResource())函数(重点关注的方法)。所需要的两个参数其实就是一个Resource经过封装成encodedResource,以及一个输入流文件。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Loading XML bean definitions from " + encodedResource);
        }

        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if (!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;//A
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }

                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if (((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }

接下来做两件事

  1. 加载xml文件获得Document对象、
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            Document doc = this.doLoadDocument(inputSource, resource);//A
            int count = this.registerBeanDefinitions(doc, resource);//B
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + count + " bean definitions from " + resource);
            }

            return count;
        } catch (BeanDefinitionStoreException var5) {
            throw var5;
        } catch (SAXParseException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
        } catch (SAXException var7) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
        } catch (ParserConfigurationException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
        } catch (IOException var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
        } catch (Throwable var10) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
        }
    }

以上代码A行根据InputSource , Resource 转换成Document对象。
使用private DocumentLoader documentLoader = new DefaultDocumentLoader();的loadDocument函数去解析
解析成Document不是我们要关注的重点,也不是spring特有的,所以这里不多加深入了解

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
    }
  1. 根据Document对象注册Bean信息
    以上代码B行根据docment文件提取以及注册Bean。
    注册bean的任务由
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //创建一个能实现注册bean的对象
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        //记录之前的环境中bean的个数
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        //重点方法
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        //记录该次加载的bean的个数
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }

跟进以下重点方法,注册bean

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.doRegisterBeanDefinitions(doc.getDocumentElement());
    }
protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }

        this.preProcessXml(root);
        //A
        this.parseBeanDefinitions(root, this.delegate);
        this.postProcessXml(root);
        this.delegate = parent;
    }

A行以上的代码都不是我们所关心的。重点看A行代码

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)) {
                        //解析默认元素
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        //解析定制元素
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            //解析定制元素
            delegate.parseCustomElement(root);
        }

    }

因为是加载bean文件所以肯定是解析默认的元素,接着往下

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if (delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if (delegate.nodeNameEquals(ele, "bean")) {
            this.processBeanDefinition(ele, delegate);
        } else if (delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }
    }

以上代码是分别对四种标签的解析而采用不同的解析方式。
对于不同标签的解析:
综上所述总结一下Spring到现在为止都做了些什么:

  1. 根据html创建Resource对象
  2. 构造XmlBeanFactory对象:创建XmlBeanDefinitionReader,开始加载资源resource
  3. 对参数Resource进行EncodedResource封装
  4. 获得Resource相应的inputStream
  5. 根据Resource加载xml文件获得Document对象
  6. 根据Document对象注册Bean信息
  7. 将每个元素进行分类:默认元素,定制元素
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!