- 以FileSystemXmlApplicationContext为例说明IoC容器初始过程
- FileSystemXmlApplicationContext构造方法中refresh()是IoC容器启动的入口
- 启动过程将包含三大步骤
- Resource定位
- BenDefinition载入和解析
- BeanDefinition在IoC中注册
Resource资源定位
-
由ResourceLoader通过Resource接口定位资源,Resouce接口有如下实现,通过名字就能猜到大概都能通过哪些方式定位资源
图片.png
-
FileSystemXmlApplicationContext通过继承DefaultResourceLoader具备了定位Resouce功能,下面这张继承关系图非常重要
图片.png
-
DefaultResourceLoader实现了ResourceLoader接口
图片.png
-
refresh()载入了BeanDefiniation,会在载入章节详细说明
图片.png
-
应用于文件系统的Resource实现,在BeanDefiniationReader.loadBeanDefination()中被调用loadBeanDefination()采用了模板方法,具体Resource定位方式由子类决定,在org.springframework.core.io.DefaultResourceLoader中被调用
图片.png
-
创建DefaultListableBeanFactory和加载配置文件在org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()完成
图片.png
-
分析一下是如何调用到AbstractRefreshableApplicationContext.refreshBeanFactory()呢,FileSystemXmlApplicationContext在构造中调用了refresh(),refresh()在org.springframework.context.support.AbstractApplicationContext被调用
图片.png
-
refresh()中有一个重要的方法obtainFreshBeanFactory(),调用到org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()
图片.png
-
在回到org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()中(回看前面的FileSystemXmlApplicationContext结成结构,对于下面的调用理解很重要),createBeanFactory()创建了DefaultListableBeanFactory,这是一个默认的IoC实现
图片.png
-
org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory)中装载BeanDefination
图片.png
-
具体的解析过程在org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(String, Set<Resource>)定义
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//对Resource路径进行解析,如Ant风格路径
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
BenDefinition载入和解析
- BenDefinition是IoC容器中保存Bean信息的一种数据结构,IoC容器对Bean的管理和注入其本质上都是对BeanDefiniation的操作
- 在FileSystemXmlApplicationContext构造方法中调用了父类的refresh(),这个方法执行过程就是整个Bean的生命周期
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// 在子类中调用refreshBeanFactory()
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
//设置BeanFacotry后置处理
postProcessBeanFactory(beanFactory);
//调用BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean后置处理器,在Bean创建过程中调用
registerBeanPostProcessors(beanFactory);
// 初始化上小文消息
initMessageSource();
// 初始化上下文事件
initApplicationEventMulticaster();
// 初始化特殊bean
onRefresh();
// 注册Bean监听器
registerListeners();
// 实例化单例bean
finishBeanFactoryInitialization(beanFactory);
// 结束容器初始化,发布容器事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已经创建的单例bean
destroyBeans();
// 重置标志位
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
-
上节中已经看到过是如何加载BeanDefiniation了,FileSystemXmlApplicationContext默认使用的IoC容器是DefaultListableBeanFactory
图片.png
-
配置文件读取在org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource, Resource)中,registerBeanDefinitions()对BeanDefinitation解析过程,也就是Spring配置文件中配置Bean的规则
图片.png
-
BeanDefiniation载入过程分为两部分,首先创建XML的解析器document,在按照Spring配置文件规则解析,具体的解析过程在org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document, XmlReaderContext)中,
图片.png
-
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element, BeanDefinitionParserDelegate)中想IoC容器注册BeanDefiniation,BeanDefinitionHolder封装了BeanDefiniation,增加了bean的名字和别名,bean的解析在BeanDefinitionParserDelegate完成,解析过程十分繁琐,参考Spring配置文件的配置规则
图片.png
BeanDefinition在IoC中注册
-
经过Resource定位,和BeanDefiniation的载入和解析,BeanDefiniation已经在IoC容器中了,但还不能使用,需要向IoC容器注册,在DefaultListableBeanFactory中通过一个Map维护这些BeanDefiniation
图片.png
- 注册过程在org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(String, BeanDefinition)中
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//如果出现beanName相同的情况,抛异常
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
- 在org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(String, BeanDefinition)中填充beanDefinitionMap,现在DefaultListableBeanFactory已经建立完整的Bean的配置信息,IoC以后要做的事就是对这些beanDefinitionMap中的数据进行检索和使用,这些数据也是IoC容器依赖注入的基础
spring容器和装配Bean:
1、容器是spring的核心,使IoC管理所有和组件
2、spring的两种容器:a、BeanFactoy
b、ApplicationContext应用上下文
3、BeanFactory:BeanhFactory使用延迟加载所有的Bean,为了从BeanhFactory得到一个Bean,只要调用
getBean()方法,就能获得Bean
4、ApplicationContext:a、提供文本信息解析,支持I18N
b、提供载入文件资源的通用方法
c、向注册为监听器的Bean发送事件
d、ApplicationContext接口扩展BeanFactory接口
e、ApplicationContext提供附加功能
5、ApplicationContext的三个实现类:a、ClassPathXmlApplication:把上下文文件当成类路径资源
b、FileSystemXmlApplication:从文件系统中的XML文件载入上
下文定义信息
c、XmlWebApplicationContext:从Web系统中的XML文件载入上下
文定义信息
6、在默认情况下,Bean全都是单态,在<bean>中的singleton为false
7、<bean>中的id属性必须遵循Java规范,而name属性可以不遵循
8、Bean的实例化的时候需要一些初始化的动作,同样当不再使用的时候,需要从容器中将其销毁
9、对象的初始化:<bean init-method="方法名">
10、对象的销毁:<bean destroy-method="方法名">
销毁对象的过程:a、主线程先被中断
b、Java虚拟机使用垃圾回收机制回收Bean对象
11、Bean设置:设值注入:1)简单配置:<bean id="xxx" class="Bean的全称类名">
<property name="xx" value="xxxxx"></property>
</bean>
value中的值可以是基本数据类型或者String类型,spring将会
自动判断设置的类型并且
将其转换成合适的值
2)引用配置:<bean id="xxx" class="Bean的全称类名">
<property name="xx" ref="xxxxx"></property>
</bean>
ref中的值是引用数据类型,spring容器会完成获取工作
3)List和数组:<bean id="xxx" class="Bean的全称类名">
<property name="list">
<list>
<value></value>
<ref bean=""/>
</list>
</property>
</bean>
list元素内可以是任何元素,但不能违背Bean所需要的对象类
型
4)Set配置:和<list>一样,将<list>改成<set>
5)Map配置:Map中的每条数据是由一个键和一个值组成,用<entry>元素来定
义
<bean id="xxx" class="Bean的全称类名">
<property name="list">
<entry key="key1">
<value></value>
</entry>
</property>
</bean>
<bean id="xxx" class="Bean的全称类名">
<property name="list">
<entry key="key1">
<ref bean=""/>
</entry>
</property>
</bean>
注意:配置entry时,属性key的值只能是String,因为Map通常用
String作为主键
6)Properties配置:使用<props>和<map>相似,最大区别是<prop>的值都是
String
注意:使用设值注入必须要有set方法,通过name属性找set方法
优势:a、通过set()方法设定更直观,更自然
b、当依赖关系较复杂时,使用set()方法更清晰
构造子注入:必须要有无参和带参的构造方法,加上index(值从0开始)属性避免注入混
淆
<constractor-arg>
注意:设值注入可以和构造子注入混合使用。先调用构造方法产生对象,再调用set()方法
赋值。但只使用设值注入时,会先调用无参的构造方法
优势:a、依赖关系一次性设定,对外不准修改
b、无需关心方式
c、注入有先后顺序,防止注入失败