spring ioc容器初始化
ioc容器创建过程:定位、载入、注册
XmlBeanFactory
低级的IOC容器
//根据 Xml 配置文件创建 Resource 资源对象,该对象中包含了 BeanDefinition 的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");
//创建 DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
//创建 XmlBeanDefinitionReader 读取器,用于载入 BeanDefinition。之所以需要 BeanFactory 作为参数,是因为会将读取的信息回调配置给factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
//XmlBeanDefinitionReader 执行载入 BeanDefinition 的方法,最后会完成 Bean 的载入和注册。完成后 Bean 就成功的放置到 IOC 容器当中,以后我们就可以从中取得 Bean 来使用
reader.loadBeanDefinitions(resource);
ApplicationContext是高级的ioc容器
FileSystemXmlApplicationContext
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh,
ApplicationContext parent)
throws BeansException {
super(parent); // 为容器设置好 Bean 资源加载器。
setConfigLocations(configLocations); //设置 Bean 定义资源文件的定位路径。
if (refresh) {
refresh(); //载入
}
}
- AbstractApplicationContext
1.super(parent); 调用父类可以看到初始化spring资源加载器
//获取一个 Spring Source 的加载器用于读入 Spring Bean 定义资源文件
protected ResourcePatternResolver getResourcePatternResolver() {
//AbstractApplicationContext 继承 DefaultResourceLoader,因此也是一个资源加载器
//Spring 资源加载器,其 getResource(String location)方法用于载入资源
return new PathMatchingResourcePatternResolver(this);
}
2.refresh载入
refresh()是一个模板方法,
refresh()方法的作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。refresh 的作用类似于对 IOC 容器的重启,在新建立好的容器中对容器进行初始化,对 Bean 定义资源进行载入。
refresh()方法主要为 IOC 容器 Bean 的生命周期管理提供条件,Spring IOC 容器载入 Bean 定义资源文件从其子类容器的 refreshBeanFactory()方法启动,之后代码都是注册容器的信息源和生命周期事件。
- AbstractBeanDefinitionReader
loadBeanDefinitions方法
//将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
//加载单个指定位置的 Bean 定义资源文件
Resource resource = resourceLoader.getResource(location);
//委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resource);
此时getResource是资源定位,调用的是 DefaultResourceLoader 中的 getSource()方法定位 Resource
资源载入2个过程:
1.通过调用 XML 解析器将 Bean 定义资源文件转换得到 Document 对象
2. 在完成通用的 XML 解析之后,按照 Spring 的 Bean 规则通过BeanDefinitionReader(DefaultBeanDefinitionDocumentReader)对 Document 对象进行解析,转换为ioc能识别的数据结构BeanDefinition。
3.将BeanDefinition注册到容器中。
- DefaultBeanDefinitionDocumentReader
元素解析
//使用Spring的Bean规则解析Document元素节点
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果元素节点是<Import>导入元素,进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果元素节点是<Alias>别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
//按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
解析Bean定义资源Document对象的普通元素
//解析Bean定义资源Document对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
// 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
// BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//todo 注册入口
//向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
对Document对象中Bean元素的解析由BeanDefinitionParserDelegate实现,注册入口BeanDefinitionReaderUtils.registerBeanDefinition。
- 总结:
IOC容器初始化基本步骤
1.初始化入口容器实现类中refresh()方法
2.在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,新建容器进行初始化。对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition。
大致过程是:通过 ResourceLoader来完成资源文件位置的定位,其中DefaultResourceLoader是默认的实现,可以从类路径,文件系统,URL 等方式来定位资源位置。通过调用 XML 解析器将 Bean 定义资源文件Resource转换得到 Document 对象。通过BeanDefinitionReader(DefaultBeanDefinitionDocumentReader)对 Document 对象进行解析,转换为ioc能识别的数据结构BeanDefinition。
往往使用的是 XmlBeanDefinitionReader 来解析,实际的处理过程是委托给 BeanDefinitionParserDelegate来完成的,从而得到BeanDefinition。最后进行IOC容器注册,就是保存到ConcurrentHashMap。注册的过程中需要线程同步,以保证数据的一致性,以后对bean的所有操作都是围绕这个
ConcurrentHashMap。
- BeanFactory 和 FactoryBean区别
其中 BeanFactory 指的是 IOC 容器的编程抽象,比如ApplicationContext,XmlBeanFactory 等,这些都是 IOC 容器的具体表现。
FactoryBean 是创建对象的工厂 Bean,只是一个可以在 IOC 容器中被管理的bean,可以看做一个抽象工厂,容器不会返回 FactoryBean 本身,而是返回其生成的对象。Spring 包括了大部分的通用资源和服务访问,都是FactoryBean 的具体实现,其中包括:对 JNDI 查询的处理,对代理对象的处理,对事务性代理的处理,对 RMI 代理的处理等,这些我们都可以看成是具体的工厂,看成是 Spring 为我们建立好的工厂。