spring源码分析获取document

wuxiaofeng      2022-04-06     627

关键词:

摘要:本文结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。

 

这一篇开始进行Document加载了,XmlBeanFactoryReader类对于文档读取并没有亲历亲为,而是委托给了DocumentLaoder去执行,DocumentLoader是个接口,真正调用的是DefaultDocumentLoader,解析代码如下:

/**
 * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
 * XML parser.
 */
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

 对于这部分代码其实并没有太多可以描述的,因为通过SAX解析XML文档的套路都差不多,Spring在这里并没有什么特殊的地方,同样首先创建DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象。这里有必要提及一下EntityResolver,对于参数entityResolver,传入的是通过getEntityResolver() 函数获取的返回值,如下代码:

protected EntityResolver getEntityResolver() {
    if (this.entityResolver == null) {
        // Determine default EntityResolver to use.
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader != null) {
            this.entityResolver = new ResourceEntityResolver(resourceLoader);
        }
        else {
            this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
        }
    }
    return this.entityResolver;
}

那么,EntityResolver到底是做什么用的呢?

EntityResolver用法

在loadDocument方法中涉及一个参数EntityResolver,何为EntitiResolver?官网这样解释:如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例。也就是说,对于解析一个XML,SAX首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。默认的寻找规则,即通过网络(实现上就是声明的DTD的URL地址)来下载相应的DTD声明,并进行认证。下载的过程漫长,而且当网络中断或不可用的时候,这里会报错,就是因为相应的DTD声明没有被找到的原因。

enntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程,比如我们将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX即可。这样就避免了通过网络来寻找相应的声明。

首先看enntityResolver的接口方法声明:

public abstract InputSource resolveEntity (String publicId, String systemId)
        throws SAXException, IOException;

这里,它接受两个参数publicId和systemId,并返回一个inputSource对象。这里我们以特定配置文件来进行讲解。

(1)如果我们在解析验证模式为XSD的配置文件,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd">

    ......
</beans>

读取到以下两个参数。

  • publicId:null
  • systemId:http://www.springframework.org/schema/beans/spring-beans.xsd

(2)如果我们在解析验证模式为DTD的配置文件,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN" "http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans">
    ......
</beans>

读取到以下两个参数:

  • publicId:-//Spring//DTD BEAN 2.0//EN
  • systemId:http://www.springframework.org/schema/beans/spring-beans.xsd

之前已经提到过,验证文件默认的加载方式是通过URL进行网络下载,这样会造成延时,用户体验也不好,一般的做法是将验证文件放置在自己的工程里,那么怎么做才能将这个URL转换为自己工程里对应的地址文件呢?我们以加载DTD文件为例来看看Spring中是如何实现的。根据之前Spring中通过getEntityResolver()方法对EntityResolver的获取,我们知道,Spring中使用DelegatingEntityResolver类为EntityResolver的实现类,resolveEntity实现方法如下:

@Override
@Nullable
public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException {
    if (systemId != null) {
        if (systemId.endsWith(DTD_SUFFIX)) {
            // 如果是dtd从这里解析
            return this.dtdResolver.resolveEntity(publicId, systemId);
        }
        else if (systemId.endsWith(XSD_SUFFIX)) {
            // 通过调用META-INF/Spring.schemas解析
            return this.schemaResolver.resolveEntity(publicId, systemId);
        }
    }
    return null;
}

我们可以看到,对不同的验证模式,Spring使用了不同的解析器解析。这里简单描述一下原理,比如加载DTD类型的BeanDtdResolver的resolveEntity是直接截取systemId最后的xx.dtd然后去当前路径下寻找,而加载XSD类型的PluggableSchemaResolver类的resolveEntity是默认到META-INF/Spring.schemas文件中找到systemId所对应的XSD文件并加载。下面是BeansDtdResolver的源码:

@Override
@Nullable
public InputSource resolveEntity(String publicId, @Nullable String systemId) throws IOException {
    if (logger.isTraceEnabled()) {
        logger.trace("Trying to resolve XML entity with public ID [" + publicId +
                "] and system ID [" + systemId + "]");
    }
    if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
        int lastPathSeparator = systemId.lastIndexOf(‘/‘);
        int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
        if (dtdNameStart != -1) {
            String dtdFile = DTD_NAME + DTD_EXTENSION;
            if (logger.isTraceEnabled()) {
                logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
            }
            try {
                Resource resource = new ClassPathResource(dtdFile, getClass());
                InputSource source = new InputSource(resource.getInputStream());
                source.setPublicId(publicId);
                source.setSystemId(systemId);
                if (logger.isDebugEnabled()) {
                    logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
                }
                return source;
            }
            catch (IOException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
                }
            }

        }
    }

    // Use the default behavior -> download from website or wherever.
    return null;
}

 

spring源码分析(十三)缓存中获取单例bean

摘要:本文结合《Spring源码深度解析》来分析Spring5.0.6版本的源代码。若有描述错误之处,欢迎指正。 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了。前面已经提到过,单例在Spring的同一个容器内只会被创建一... 查看详情

spring5源码分析系列——ioc容器的初始化

...BeanDefinition在IOC容器中的注册接下来分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中,在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitio 查看详情

[死磕spring6/43]---ioc之获取document对象(代码片段)

[死磕Spring6/43]—IOC之获取Document对象https://www.cmsblogs.com/article/1391375327816716288定位本编文章3/5BeanDefinitionReader,XmlBeanDefinitionReader.loadBeanDefinitions()介绍简介在XmlBeanDefinitionReader.doLoadDocument()方法中做了两件事情,一是调用getValidatio... 查看详情

spring源码分析

...3、我自己试着写了一个这个接口的实现类:packagecom.lzh.spring.test;importjava.util.ArrayList;importjava.util.Hashtab 查看详情

spring源码分析之ioc容器是如何创建和获取单实例bean的

首先创造ioc容器,这条语句一执行,所有的bean都已经创建好了,并存放在了ioc的容器中。ApplicationContextioc=newClassPathXmlApplicationContext("beans.xml");ClassPathXmlApplicationContext()方法点开ClassPathXmlApplicationContext()类,我们发现无论构造器都... 查看详情

spring源码分析注册解析的beandefinition

摘要:本文结合《Spring源码深度解析》来分析Spring5.0.6版本的源代码。若有描述错误之处,欢迎指正。 对配置文件解析完成后,获取的beanDefiniton已经可以进行使用了,剩下的唯一工作就是注册了,也就是processBeanDefinition方法... 查看详情

spring源码分析系列applicationcontext相关接口架构分析

...文地址】http://www.cnblogs.com/zffenger/p/5813470.html 在使用Spring的时候,我们经常需要先得到一个ApplicationContext对象,然后从该对象中获取我们配置的Bean对象。ApplicationContext隶属于org.springframework.context,是SpringFramew 查看详情

spring5源码分析系列——ioc容器的初始化

前言:上一篇讲到了DocumentLoader将Bean定义资源转换为Document对象,此篇我们继续后面的内容。(9)XmlBeanDefinitionReader解析载入的Bean定义资源文件XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML文件中实际载入Bean定义资源的... 查看详情

spring源码之将xml文件解析为document对象

最近准备把spring,springmvc,mybtais,springboot,部分的cloud组件的源码重头梳理一下,并在语雀记录一下。Spring源码之将Xml文件解析为Document对象(三):https://www.yuque.com/dalianpai/spring/qlsqkv 查看详情

linux内核获取初次编译源码目录分析(代码片段)

...ux内核源码目录分析1、arch目录2、block目录3、crypto目录4、Documentation目录5、drivers目录6、firmware目录7、fs目录8、include目录9、init目录10、ipc目录11、kernel目录12、lib目录13、mm目录Linux内核获取关于Linux的起源以及发展历史,这里... 查看详情

spring源码分析ioc容器初始化

前言:经过前几篇文章的讲解,我们已经得到了BeanDefinition,接下来将分析Bean的加载。获取Bean的入口:AbstractApplicationContext#getBean1publicObjectgetBean(Stringname)throwsBeansException{2//检测bean工厂是否存活3assertBeanFactoryActive();4returngetB 查看详情

spring源码分析专题——目录

Spring源码分析专题——阅读指引IOC容器Spring源码分析专题——IOC容器启动过程(上篇)Spring源码分析专题——IOC容器启动过程(中篇)Spring源码分析专题——IOC容器启动过程(下篇)Spring源码分析专题——IOC容器依赖注入SpringMVC... 查看详情

spring源码分析总结

Spring容器的refresh()【创建刷新】;1、prepareRefresh()刷新前的预处理; 1)、initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法; 2)、getEnvironment().validateRequiredProperties();检验属性的合法等 3)、earlyApplicationEvents=n... 查看详情

源码分析spring-mvc启动流程

参考技术Aspring-mvc的启动流程1、request请求到达 dispatchServlet->doService()->doDispatch() 开始处理请求2、根据doDispatch()再去调用getHandler()目的是获取包含处理器Handler和处理器拦截器AdapterIntercepers的处理器拦截链HandlerExecutionCh... 查看详情

spring事务源码分析专题jdbctemplate使用及源码分析

Spring中的数据访问,JdbcTemplate使用及源码分析前言本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成对源码分析前,我希望先介绍一下Spring中数据访问的相关内容,然后层层递进到事物的源码分析,主要分... 查看详情

spring源码分析——源码分析环境搭建

...adle工具类似于maven,用于项目的构建,此处主要用于构建spring源码,以便我们将spring源码导入eclipse。       开发环境Java:JDK8(必须是JDK或JRE7以上,使用java-version查看 查看详情

spring循环引用源码分析

Spring循环引用(二)源码分析Spring系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)Spring循环引用相关文章:《Spring循环引用(一)一个循环依赖引发的BUG》:https://www.cnblogs.com/binarylei/p/10325698.html《Spring循环引用(二)源码分析... 查看详情

09spring源码-分析篇-di源码分析(代码片段)

Spring源码-DI的过程  接下来我们分析下Spring源码中Bean初始化过程中的DI过程。也就是属性的依赖注入。一、构造参数依赖1.如何确定构造方法  在Spring中生成Bean实例的时候默认是调用对应的无参构造方法来处理。@Componentp... 查看详情