本文作者:chenssy
出处:http://cmsblogs.com/?p=2658
在学习
Spring源码
的过程中发现的好站+好贴,感谢作者。Spring版本:Spring 5.0.6.RELEASE
开局先看一段代码:
ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
这段代码是 Spring
中编程式使用 IOC
容器,通过这四段简单的代码,我们可以初步判断 IOC
容器的使用过程。
获取资源
获取
BeanFactory
根据新建的
BeanFactory
创建一个BeanDefinitionReader
对象,该Reader
对象为资源的解析器装载资源 整个过程就分为三个步骤:资源定位、装载、注册,如下:
资源定位。我们一般用外部资源来描述
Bean
对象,所以在初始化IOC
容器的第一步就是需要定位这个外部资源。在( IOC 之 Spring 统一资源加载策略)已经详细说明了资源加载的过程。装载。装载就是
BeanDefinition
的载入。BeanDefinitionReader
读取、解析Resource
资源,也就是将用户定义的Bean
表示成IOC
容器的内部数据结构:BeanDefinition
。在IOC
容器内部维护着一个BeanDefinition Map
的数据结构,在配置文件中每一个 都对应着一个BeanDefinition
对象。注册。向
IOC
容器注册在第二步解析好的BeanDefinition
,这个过程是通过BeanDefinitionRegistry
接口来实现的。在IOC
容器内部其实是将第二个过程解析得到的BeanDefinition
注入到一个HashMap
容器中,IOC
容器就是通过这个HashMap
来维护这些BeanDefinition
的。在这里需要注意的一点是这个过程并没有完成依赖注入,依赖注册是发生在应用第一次调用getBean()
向容器索要Bean
时。当然我们可以通过设置预处理,即对某个Bean
设置lazyinit
属性,那么这个Bean
的依赖注入就会在容器初始化的时候完成。
XML Resource => XML Document => Bean Definition 。
资源定位在前面已经分析了,下面我们直接分析加载,上面提过reader.loadBeanDefinitions(resource)
才是加载资源的真正实现,所以我们直接从该方法入手。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
从指定的 xml
文件加载 BeanDefinition
,这里会先对 Resource
资源封装成 EncodedResource
。这里为什么需要将 Resource
封装成 EncodedResource
呢?主要是为了对 Resource
进行编码,保证内容读取的正确性。封装成 EncodedResource
后,调用loadBeanDefinitions()
,这个方法才是真正的逻辑实现。如下:
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<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 将当前资源加入记录中
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of "
+ encodedResource + " - check your import definitions!");
}
try {
// 从 EncodedResource 获取封装的 Resource 并从 Resource 中获取其中的 InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
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();
}
}
}
首先通过
resourcesCurrentlyBeingLoaded.get()
来获取已经加载过的资源,然后将 encodedResource
加入其中,如果 resourcesCurrentlyBeingLoaded
中已经存在该资源,则抛出 BeanDefinitionStoreException
异常。完成后从 encodedResource
获取封装的 Resource
资源并从 Resource
中获取相应的 InputStream
,最后将 InputStream
封装为 InputSource
调用 doLoadBeanDefinitions()
。
方法 doLoadBeanDefinitions()
为从 xml
文件中加载 BeanDefinition
的真正逻辑,如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 获取 Document 实例
Document doc = doLoadDocument(inputSource, resource);
// 根据 Document 实例****注册 Bean信息
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);
}
}
核心部分就是 try
块的两行代码。
- 调用
doLoadDocument()
方法,根据xml
文件获取Document
实例。 - 根据获取的
Document
实例注册Bean
信息。 其实在
doLoadDocument()
方法内部还获取了 xml
文件的验证模式。如下:
protected Document doLoadDocument(InputSource inputSource, Resource resource)
throws Exception {
return this.documentLoader.loadDocument(inputSource,
getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource),
isNamespaceAware());
}
调用getValidationModeForResource()
获取指定资源(xml
)的验证模式。
所以 doLoadBeanDefinitions()
主要就是做了三件事情。
- 调用
getValidationModeForResource()
获取xml
文件的验证模式 - 调用
loadDocument()
根据xml
文件获取相应的Document
实例。 - 调用
registerBeanDefinitions()
注册Bean
实例。

相关推荐
版权声明
- 本文链接: https://www.cayzlh.com/2020/01/03/a6d994a0.html
- 版权声明: 文章内容仅用作学习和分享,如有雷同,纯属拷贝。