转载自:
http://blog.youkuaiyun.com/chjttony/article/details/6248682
http://blog.youkuaiyun.com/chjttony/article/details/6263347
http://blog.youkuaiyun.com/chjttony/article/details/6278627
Spring Bean的加载依赖于BeanFactory,但实际使用时不会直接使用BeanFactory,而是采用ApplicationContext这个在BeanFactory基础上提供了扩展的接口:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,
HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
ApplicationContext接口具体的实现类常用的有:FileSystemXmlApplicationContet (从文件系统中读入Bean定义资源文件)、ClassPathXmlApplicationContext(从Classpath类路径中读入Bean定义资源文件)和XmlWebApplicationContext(从Web容器如Tomcat等中读入Bean定义资源文件)等。
以FileSystemXmlApplicationContext
为例,通过分析FileSystemXmlApplicationContext
及其父类的源码来分析IoC容器定位Bean定义资源文件的实现过程:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
//configLocation是Bean定义资源文件路径
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
//调用父类AbstractRefreshableConfigApplicationContext的方法,
//设置Bean定义的资源文件,完成IoC容器Bean定义资源的定位
setConfigLocations(configLocations);
if (refresh) {
//调用父类AbstractApplicationContext的refresh()
//函数启动载入Bean定义的过程,是Ioc容器载入Bean定义的入口
refresh();
}
}
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
通过代码可以知道,在创建FileSystemXmlApplicationContext
容器时,首先,调用父类容器的构造方法super(parent)
方法为容器设置好Bean资源加载器。然后,再调用setConfigLocations(configLocations)
方法设置Bean定义资源文件的定位路径。
Spring IoC容器对Bean定义资源的载入是从refresh()
函数开始的,FileSystemXmlApplicationContext
通过调用其父类AbstractApplicationContext
的refresh()
函数开始整个IoC容器对Bean定义的载入过程。AbstractApplicationContext#refresh()
载入Bean定义过程:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
//创建bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为BeanFactory配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
//调用所有注册的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
//为BeanFactory注册BeanPost事件处理器.
//BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
//初始化信息源,和国际化相关.
initMessageSource();
//初始化容器事件传播器.
initApplicationEventMulticaster();
//调用子类的某些特殊Bean初始化方法
onRefresh();
//为事件传播器注册事件监听器.
registerListeners();
//初始化所有剩余的单态Bean.
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
//销毁以创建的单态Bean
destroyBeans();
//取消refresh操作,重置容器的同步标识.
cancelRefresh(ex);
throw ex;
}
}
}
refresh()
方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()
方法启动,所以refresh()
中obtainFreshBeanFactory()
是载入过程的开始:
//AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里是关键的 refreshBeanFactory 方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
AbstractApplicationContext
的obtainFreshBeanFactory()
方法调用refreshBeanFactory()
方法,启动容器载入Bean定义资源文件的过程。AbstractApplicationContext
类中只抽象定义了refreshBeanFactory()
方法,容器真正调用的是其子类AbstractRefreshableApplicationContext#refreshBeanFactory()
,方法的源码如下:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IoC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入Bean定义的方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " +
getDisplayName(), ex);
}
}
refreshBeanFactory()
方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。
createBeanFactory()
是创建了DefaultListableBeanFactory
工厂,载入BeanDefinition的方法loadBeanDefinitions
只是AbstractRefreshableApplicationContext
类中的抽象方法。
AbstractRefreshableApplicationContext
的子类有XmlWebApplicationContext
、AnnotationConfigWebApplicationContext
。看类名就大概知道分别是解析哪种类型的配置。
解析好的BeanDefinition,由 BeanDefinitionReaderUtils.registerBeanDefinition()
注册到DefaultListableBeanFactory
中的beanDefinitionMap
:
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<String, BeanDefinition>(256);
注意上里的BeanDefinition
只是解析后的类Bean定义的相关数据,并没进行依赖注入,依赖注入在以下两种情况发生:
(1)用户第一次通过getBean方法向IoC容索要Bean时,IoC容器触发依赖注入。
(2)当用户在Bean定义资源中为<Bean>元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。