【死磕 Spring】----- IOC 之 加载 Bean

先看一段熟悉的代码:

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 容器的第一步就是需要定位这个外部资源。在上一篇博客(【死磕 Spring】----- IOC 之 Spring 统一资源加载策略)已经详细说明了资源加载的过程。
装载。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IOC 容器的内部数据结构:BeanDefinition。在 IOC 容器内部维护着一个 BeanDefinition Map 的数据结构,在配置文件中每一个 <bean> 都对应着一个BeanDefinition对象。
注册。向IOC容器注册在第二步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistery 接口来实现的。在 IOC 容器内部其实是将第二个过程解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IOC 容器就是通过这个 HashMap 来维护这些 BeanDefinition 的。在这里需要注意的一点是这个过程并没有完成依赖注入,依赖注册是发生在应用第一次调用 getBean() 向容器索要 Bean 时。当然我们可以通过设置预处理,即对某个 Bean 设置 lazyinit 属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。
资源定位在前面已经分析了,下面我们直接分析加载,上面提过 reader.loadBeanDefinitions(resource) 才是加载资源的真正实现,所以我们直接从该方法入手。

public

int
loadBeanDefinitions
(
Resource
resource
)

throws

BeanDefinitionStoreException

{

return
loadBeanDefinitions
(
new

EncodedResource
(
resource
));

}
从指定的 xml 文件加载 Bean Definition,这里会先对 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 文件中加载 Bean Definition 的真正逻辑,如下:

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 实例。
获取Java高级进阶资料,可加群828545509

原文地址:http://blog.51cto.com/13942046/2177172

时间: 2024-10-10 13:03:34

【死磕 Spring】----- IOC 之 加载 Bean的相关文章

【死磕 Spring】----- IOC 之 获取验证模型

原文出自:http://cmsblogs.com 在上篇博客[死磕Spring]----- IOC 之 加载 Bean 中提到,在核心逻辑方法 doLoadBeanDefinitions()中主要是做三件事情. 调用 getValidationModeForResource() 获取 xml 文件的验证模式 调用 loadDocument() 根据 xml 文件获取相应的 Document 实例. 调用 registerBeanDefinitions() 注册 Bean 实例. 这篇博客主要分析

spring IOC之篇六 bean的加载---bean的创建

之前我们讲解了缓存中单例的获取,解下来需要讲解缓存中不存在该如何处理 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); // 全局变量需要同步 synchronized (this.singletonObjects) { // 先从单例缓存池获取,以便

Spring之IOC容器加载初始化的方式

引言 我们知道IOC容器时Spring的核心,可是如果我们要依赖IOC容器对我们的Bean进行管理,那么我们就需要告诉IOC容易他需要管理哪些Bean而且这些Bean有什么要求,这些工作就是通过通过配置文件告诉Spring 的IOC容器.在我们的完成这些配置文件以后,如果IOC容易要实现对这些Bean的管理,除了资源的定位还有一个很重要的一步就是完成IOC加载初始化,也就是配置文件的载入过程.完成IOC容器加载初始化的方式只要有三种,第一种就是通过File文件加载,第二种是通过Classpath

spring加载bean的步骤

转载:https://www.cnblogs.com/hljmly/articles/5442778.html spring加载bean的方法 用spring的时候,需要一个applicationContext.xml文件,放到工程目录src下,每次我想获得这个xml文件里的bean的时候,就要这样写: BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml"); 因为是放

Spring多种加载Bean方式简析

1 定义bean的方式 常见的定义Bean的方式有: 通过xml的方式,例如: <bean id="dictionaryRelMap" class="java.util.HashMap"/> 通过注解的方式,在Class上使用@Component等注解,例如 @Component public class xxxServicer{ .... } 通过在@Configuration类下的@Bean的方式,例如 @Configuration public c

死磕Spring AOP系列2:剖析Bean处理器之BeanNameAutoProxyCreator

通过前一篇<死磕Spring AOP系列1:编程式实现AOP>,学习了Spring对代理的底层支持,认识了ProxyFactory对象,及从类设计层面认识了PointCut&Advisor&Advice&Interceptor,还认识了AdvisorChainFactory对象,知道了底层Advisor的底层链式结构.但是,上篇我们仅仅是通过Spring编程式实现的"AOP"效果,这种方式,实际开发时,如果这样用就太LOW了.今天,主要认识一个生成代

死磕Spring AOP系列3:剖析Bean处理器之DefaultAdvisorAutoProxyCreator

导航 死磕Spring AOP系列1:编程式实现AOP 死磕Spring AOP系列2:剖析Bean处理器之BeanNameAutoProxyCreator 这是<死磕Spring AOP系列>的第三篇.经过前面的讲解,已经掌握了以下知识点 Spring AOP的底层支持,是基于ProxyFactory+ProxyConfig+Advisor生成的 Spring容器的代理对象生成:在Bean生命周期过长中调用BeanPostProcessor,将对象进行包装,生成代理对象. Advisor的指

spring Boot加载bean

1.SpringBoot中加载bean,可以使用注解@compenent直接加载到applicationContext容器中 2.在直接类@Configuration中,手动注册bean,如: 原文地址:https://www.cnblogs.com/javabg/p/10704566.html

从启动日志看Spring IOC的初始化和Bean生命周期

一.Tomcat中启动IoC容器的日志 启动Tomcat等容器时,控制台每次都打印出一些日志. 最近刚好在研究Spring源码,所以换个角度,从启动日志来简单的看看Spring的初始化过程! 以下是Tomcat启动时日志,截取Spring部分. //------------------------------------- //从这里开始Spring的初始化 十一月 10, 2015 8:52:03 上午 org.apache.catalina.core.ApplicationContext l