本文正式的对Ioc容器初始化一探究竟,我们可根据上篇的流程图以起对照着看Ioc容器是如何初始化的。
IOC容器的初始化(以Xml形式的Ioc初始化)
在看具体的容器初始化先看下重要的BeanDefinition的类图
6eD13Q.png
BeanDefinition是配置文件元素标签在容器中内部表示形式。
-
RootBeanDefinition
可以单独作为一个BeanDefinition
,也可以作为其他BeanDefinition
的父类。但是他不能作为其他BeanDefinition的子类 -
ChildBeanDefinition
相当于一个子类,不可以单独存在,必须要依赖一个父BeanDetintion
。 -
GenericBeanDefinition
可以替代RootBeanDefinition
和ChildBeanDefinition
-
AnnotatedGenericBeanDefinition
处理@Configuration
注解 -
ConfigurationClassBeanDefinition
处理@Bean
注解 -
ScannedGenericBeanDefinition
处理@Component
注解
IoC容器的初始化包括BeanDefinition的Resouce定位、载入和注册这三个基本的过程
BeanDefinition描述和定义了创建一个Bean需要的所有信息,属性,构造函数参数以及访问它们的方法。还有其他一些信息,比如这些定义来源自哪个类等等信息
Resouce定位: BeanDefinition的资源定位由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用提供了统一接口。比如说,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径中可以使用前面提到的ClassPathResource来使用,等等。这个过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样
BeanDefinition的载入: 第二个关键的部分是BeanDefinition的载入,该载入过程把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition,总地说来,这个BeanDefinition定义了一系列的数据来使得IoC容器能够方便地对POJO对象也就是Spring的Bean进行管理。即BeanDefinition就是Spring的领域对象模型
BeanDefinition的注册: 第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的,这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。可以看到,在IoC容器内部,是通过使用一个HashMap来持有这些BeanDefinition数据的。
BeanDefinition的Resource定位
Resource定位这个过程就是我们所看到的寻找bean定义的资源配置文件,找到”applicationContext.xml“以及其他的配置文件信息。
这时我们可以使用的是ClassPathResource,意味着Spring会在类路径中寻找以文件形式存在的BeanDefinition信息。
ClassPathResource res = new ClassPathResource("beans.xml")
使用这个代码不能让DefaultListableBeanFactory.
这个过程是使用的ApplicationContext的实现类ClassPathXmlApplicationContext,FileSystemXmlApplicationContext,WebApplicationContext
通过这些类去定位到Resource对象。
以ClassPathXmlApplicationContext获取bean为例深入源码分析
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
复制代码
当程序通过new的时候会进行调用其构造方法,在构造方法内进行资源加载,主要看构造方法内的setConfigLocations(configLocations) 进行资源定位
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
// 资源定位
setConfigLocations(configLocations);
// 载入BeanDefinition的入口
if (refresh) {
refresh();
}
}
复制代码
资源定位集中在抽象类AbstractRefreshableConfigApplicationContext的setConfigurations()方法内我们具体的看下:在这个方法内是先进行断言这个位置是否为null.在这类中初始了一个String[] configLocations数组.将解析后的路径填充到这个数组中。
/**
* 设置上下文的配置,如果未配置则 可以进行默认配置
*
**/
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
// 断言是否为空路径
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
// resolvePath为同一类中将字符串解析路径的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
/**
* 路径的解析
*/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
/**
* 获取路径
*/
@Override
public ConfigurableEnvironment getEnvironment() {
// 环境为null则进行创建环境
if (this.environment == null) {
this.environment = createEnvironment();
}
// 否者返回档期那的环境
return this.environment;
}
/**
* 创建并返回一个环境
**/
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
/**
*这个时候创建了一个标准的环境。可以看到resolvePath()方法来自AbstractEnvironment类中
*
**/
public class StandardEnvironment extends org.springframework.core.env.AbstractEnvironment {
public static final java.lang.String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final java.lang.String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
public StandardEnvironment() { /* compiled code */ }
protected void customizePropertySources(org.springframework.core.env.MutablePropertySources propertySources) { /* compiled code */ }
}
复制代码
经过上面的步骤这样就完成了Resource的定位。
本文使用 文章同步助手 同步