IOC初始化可归纳为两个步骤,创建容器、加载bean定义。本文以ApplicationContext为例讲解创建容器过程,其实现类为ClassPathXmlApplicationContext。

ApplicationContext允许嵌套上下文,可以将父上下文维护在一个上下文中,其原理很类似双亲委托加载。对于使用的话,加载一个bean,会先检查当前上下文,然后检索父上下文,逐级向上。这样为spring应用提供一个共享的bean环境,而不用在多个上下文之间来回切换。

寻找入口

ApplicationContext主要实现类很多,AnnotationConfigApplicationContext、XmlWebApplicationContext。都继承自父容器AbstractApplicationContext,最后都会调到refresh方法启动,这里以ClassPathXmlApplicationContext为例。通过main方法启动spring。

1
2
3
4
5
6
7
@Test
public void testConfigLocation() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
Service service = (Service) ctx.getBean("service");
ctx.close();
assertTrue(service.isProperlyDestroyed());
}

代码中只有主要new一个ClassPathXmlApplicationContext对象,就可以使用IOC获取bean了,那么它的初始化,那么我们只有一个入口,也就是它的构造,而在这个构造中,最后会调用以下重载构造方法。

1
2
3
4
5
6
7
8
9
10
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {

super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

加载配置

通过上面构造方法,在调用refresh()之前会调用父类构造,和调用setConfigLocations()处理传进来配置文件。
我们先看父类构造,会一直往上调用,直到AbstractApplicationContext,最后我将多个构造合并之后结果如下:

1
2
3
4
5
6
7
8
9
10
public abstract class AbstractApplicationContext ...{
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this.resourcePatternResolver = getResourcePatternResolver();
setParent(parent);
}
// 用于读取spring bean配置信息
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
}

这里其实就是准备一下工作,接下来就是setConfigLocations()了,该方法由AbstractRefreshableConfigApplicationContext实现,进行bean配置的定义。

1
2
3
4
5
6
7
8
9
10
11
12
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++) {
//解析bean定义资源文件路径
this.configLocations[i] = resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}

最重要的是resolvePath(),该方法为同一个类中将字符串解析为路劲。并将其保存。

整体启动过程

之前多多少少都应该知道spring启动是调用refresh()方法的。但是refresh()却是一个模板方法,只规定了spring启动流程,许多实现逻辑,需要深入它的子类实现中。这一节整体的看一下refresh()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 容器刷新前,预处理方法。获取启动时间,以及各种标记处理
prepareRefresh();

// 提供抽象方法refreshBeanFactory,由子类刷新BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 给BeanFactory对象配置类加载器、事件处理器等
prepareBeanFactory(beanFactory);

try {
// 提供默认方法供子类重写。用于在标准初始化之后,某些子类需要修改应用程序上下文的内部beanFactory。此时IOC初始化已经完成。
postProcessBeanFactory(beanFactory);

// 调用所以注册的BeanFactoryPostProcessor的bean
invokeBeanFactoryPostProcessors(beanFactory);

// 为BeanFacytory注册Post事件处理器,BeanPostProcessor为bean创建后置处理器,可用于监听事件。
registerBeanPostProcessors(beanFactory);

// 初始化信息源,国际化
initMessageSource();

// 初始化容器事件传播器
initApplicationEventMulticaster();

// 提供默认方法供子类重写,处理特殊bean初始化
onRefresh();

// 为事件传播器注册监听器
registerListeners();

// 初始化所有剩余的单例bean,(非懒加载的)
finishBeanFactoryInitialization(beanFactory);

// 发布IOC启动完事件
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// 销毁bean
destroyBeans();

// 需要刷新操作,重置同步标识
cancelRefresh(ex);

throw ex;
} finally {
// 重设公共缓存
resetCommonCaches();
}
}
}

其实refresh()方法主要为IOC容器的bean管理生命周期。IOC容器载入bean配置信息,由子类的refreshBeanFactory()实现,也就是说obtainFreshBeanFactory()以后的代码都在注册容器的信息源和生命周期。

创建容器

从obtainFreshBeanFactory()进入,查看其子类的AbstractRefreshableApplicationContext.refreshBeanFactory()。该方法真正完成了IOC的初始化过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected final void refreshBeanFactory() throws BeansException {
// 注销之前所有创建beanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建IOC
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 设置一些启动参数,注解装配等
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);
}
}
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

refreshBeanFactory()值得注意的是,它会先销毁之前的beanfactory,然后在createBeanFactory()方法中new一个DefaultListableBeanFactory的对象,以保证后续所有的方法,都调用先创建的beanFactory。它还做了另一件事情就是loadBeanDefinitions()这个方法用于解析bean的配置信息,然后转换成BeanDefinition对象,供spring使用。下一章再详细解说。