Spring5.x之XmlBeanFactory源码解析-解析前准备(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hjtlovelife/article/details/89377049

Spring5.x之XmlBeanFactory源码解析-XmlBeanFactory整体概览(一)

1、XmlBeanFactory实例化时设置忽略给定依赖接口的自动装配

        XmlBeanFactory实例化时首先调用父类AbstractAutowireCapableBeanFactory构造器,其中会调用ignoreDependencyInterface设置忽略给定依赖接口的自动装配。

        为什么需要忽略指定接口的自动装配?见AbstractAutowireCapableBeanFactory.ignoreDependencyInterface方法上注释:通常由应用程序上下文用于注入以其他方式解析的依赖关系,例如BeanFactory通过BeanFactoryAware或ApplicationContext通过ApplicationContextAware。默认情况下,只忽略BeanFactoryAware接口自动装配。 要忽略其它类型,请为每种类型调用此方法。

        当前bean想要获取Spring容器的BeanFactory、ApplicationContext、在容器中的beanName,我们该怎么做?相比大家想到了Spring给我们提供了*Aware接口,使得实现接口的bean有了获取BeanFactory、ApplicationContext等的能力。

        比如:AbstractAutowireCapableBeanFactory.initializeBean初始化bean的时候会检测当前bean是否实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口,从而注入BeanName、BeanClassLoader、BeanFactory,如下图:

       比如:ApplicationContextAwareProcessor.invokeAwareInterfaces在bean实例化之前会检查当前bean是否实现了指定接口并注入相应值,如下图:

        

        Spring容器在刷新的时候会调用AbstractApplicationContext.prepareBeanFactory添加BeanPostProcessor以及ignoreDependencyInterface,如下图:

   

2、XmlBeanDefinitionReader解析XML

        XmlBeanDefinitionReader解析时序图如下:

        从时序图中可知XmlBeanFactory解析XML入口是XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)

方法,将resource使用EncodedResource进行包装后调用XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)方法,进入到该方法后会检查resource是否已经被加载,避免资源之间循环引用而导致循环加载,之后就是构造org.xml.sax.InputSource并设置编码,如下图:

        进入doLoadBeanDefinitions方法后:

1、使用DocumentLoader的实现类DefaultDocumentLoader将resource转换成document

        手动指定了XML文件的验证模式则使用指定的,否则使用XmlValidationModeDetector.detectValidationMode方法获取到XML文件的验证模式。

EntityResolver用于解析XML文件使用到的DTD或者XSD文档,用于验证XML文档合法性

        如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例 。 对于解析一个XML, SAX首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。 默认的寻找规则,即通过网络(实现上就是声明的DTD的URI地址)来下载相应的DTD声明并进行认证 。下载的过程是一个漫长的过程,而且当网络中断或不可用时会报错,是因为相应的DTD声明没有被找到的原因 。
        EntityResolve的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找 DTD 声明的过程,比如我们将DTD文件放到项目中某处 ,在实现时直接将此文档读取并返回给SAX即可,这样就避免了通过网络来寻找相应的声明 。

public abstract InputSource resolveEntity (String publicId,String systemId)throws SAXException, IOException;接收两个参数 publicld和systemId ,并返回一个inputSource对象 。

解析验证模式为 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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

读取到以下两个参数 
publicld: null
systemld: http://www.springfamework.org/schema/beans/spring-beans.xsd

解析验证模式为DTD的配置文件,代码如下:

<?xrnl version= "1.0" encoding = "UTF-8" ?>
< ! DOCTYPE beans PUBLIC "- // Spring //DTD BEAN 2.0//EN" "http : //www. Springfrarnework .org/
dtd/Spring-beans-2.0.dtd">
<beans>
</beans>
读取到以下两个参数 
publicld: -//Spring/DTD BEAN 2.0//EN
systemld: http://www.springframework.org/dtd/Spring-beans-2.0.dtd
        验证文件默认的加载方式是通过URL进行网络下载获取,这样会造成延迟用户体验也不好, 一般的做法都是将验证文件放置在向己的工程里,那么怎么做才能将这个 URL 转换为自己工程里对应的地址文件呢?以加载DTD文件为例来看看 Spring 中是
如何实现的 。 根据之前 Spring 中通过 getEntityResolver()方法对 Entity Resolver 的获取,我们知道, Spring 中使用 DelegatingEntityResolver 类为 Entity Resolver 的实现类, resolveEntity 实现方法如下:

        systemld不管是XSD还是DTD验证模式都会存在的,那么可以根据systemld后缀采取不同解析逻辑,Spring提供了BeansDtdResolver、PluggableSchemaResolver来分别处理DTD、XSD文件读取逻辑。ResourceEntityResolver继承DelegatingEntityResolver,在解析的时候先调用父类解析方法,如果父类没有解析先判断systemId是否是当前路径下的文件,如果是则获取到资源相对路径,否则DTD、XSD则会走网络加载指定资源文件,详细请看ResourceEntityResolver类。

2、使用DefaultBeanDefinitionDocumentReader.registerBeanDefinitions方法解析document并注册BeanDefinitions

        首先实例化BeanDefinitionDocumentReader的接口实现类DefaultBeanDefinitionDocumentReader,调用它的registerBeanDefinitions方法用于从Document中读取解析bean

        获取到Document根元素后调用BeanDefinitionDocumentReader.doRegisterBeanDefinitions方法开始beans元素的解析。首先是对profile的处理,然后开始进行解析。在解析之前之后都是可以针对元素自定义处理逻辑。元素未定义命名空间或者命名空间是http://www.springframework.org/schema/beans则是默认命名空间。

        调用DefaultBeanDefinitionDocumentReader.createDelegate方法给当前解析的beans元素创建BeanDefinitionParserDelegate,调用BeanDefinitionParserDelegate.initDefaults方法初始化默认值,如果是嵌套beans元素则默认值取父节点的默认值。

猜你喜欢

转载自blog.csdn.net/hjtlovelife/article/details/89377049
今日推荐