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); } 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++) { 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();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try { postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); }
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 { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); 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使用。下一章再详细解说。