首先我们了解一下使用IoC容器之前的几个步骤:
- 创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息
- 创建一个BeanFactory
- 创建一个载入BeanDfinition的读取器,这里使用XMLBeanDefinitionReader对象来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory
- 从定以好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader完成;完成整个载入 和注册Bean定义之后,需要的IoC容器就建立起来了。之后就可以使用IoC容器了
IoC启动:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
//refresh()方法会牵扯到IoC启动的一系列复杂的操作;下面对refresh方法的操
作进行详细介绍
this.refresh();
}
}
实例化ClassPathXmlApplicationContext时,同时启动了IoC容器refresh启动过程
详解IoC容器refresh()启动过程
简单来说,IoC容器的初始化过程就是前面的refresh方法启动的,这个方法标志着IoC容器的正式启动; 具体来说,这个过程包括BeanDefinition的Resource定位、载入、注册三个基本过程;spring把这三个过程分开,使用不同的模块完成,如使用相应的ResourceLoader、BeanDefinitionReader等模块,通过这样的方式可以让用户更加灵活的对这三个过程进行剪裁或扩展,定义出适合自己的IoC容器的初始化过程。
1 BeanDefinition 的resource定位
这个Resource定位指的是BeanDefinition的资源定位,这个过程类似于容器寻找数据的过程;在spring中,使用的是ClassPathResource;
ClassPathResource resource =new ClassPathResource("beans.xml");
这里定义的resource不能直接由对应的BeanFactory直接使用,Spring通过BeanDefinitionReader对这些信息进行处理。
对于我们经常使用的ApplicationCntext,例如FileSystemXmlApplication,可以从文件系统载入resource;ClassPathXmlApplication可以从Class Path中载入Resource;XMLWebApplication从web容器中载入resource
FileSystemXmlApplication的源代码实现
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
2 BeanDefinition的载入和解析
完成BeanDesinition的resource的定位分析后,开始进行BeanDefinition的载入过程。对IoC容器来说,载入过程相当于把定义的BeanDefinition转换成一个Spring内部表示的数据结构的过程;IIoC容器对Bean的管理和依赖注入功能是通过对其持有的BeanDefinition进行各种操作完成的,这些BeanDefinition数据在IoC容器中通过一个HashMap来维护。
容器初始化入口refresh方法
该方法定义在AbstractApplication中,是FileSystemXmlApplication的一个基类
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//在refreshBeanFactory方法中创建了BeanFactory;在创建IoC容器前,如果有容器存在,先把存在的容器销毁和关闭,保证refresh以后新建立的IoC容器
this.refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
在refreshBeanFactory方法的实现中,调用了loadBeanDefinition的方法,启用对BeanDefinition的载入;传入参数是新建的BeanFactory
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
BeanDefinition的载入分成两个部分,首先通过调用 XML的解析器得到document对象(没有按照Spring 的Bean规则解析);
在documentReader中实现了按照Spring的Bean规则进行解析。
3 BeanDefinition在IoC容器中的注册
BeanDefinition载入和解析过程已经在IoC容器中建立了自己的数据结构和相应的数据表示,但此时这些数据还不能直接使用,需要进行注册。在DefaultListableBeanFactory中,通过HashMap来持有BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
注册的过程并不复杂,就是把解析到的BeanDefinition设置到ConcurrentHashMap中,注意:如果遇到相同名字的BeanDefinition,根据allowBeanDefinitionOverrideing配置来完成(是否能够覆盖,不能覆盖则【】抛出异常);
总结
完成了BeanDefinition的注册之后,IoC的初始化过程就完成了。此时在使用的IoC容器中已经建立起了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,他们都在beanDefinitionMap中被检索和使用。容器的作用就是对这些信息进行维护和处理;这些信息是建立依赖反转的基础。