摘要:上一篇博客对spring ioc做了大致概述,大方向上了解了spring ioc的原理,清晰了ioc的设计思路,这篇博客的定位主要是了解ioc如何进行资源定位的加载。
前文回顾:实现了refresh()方法,这个方法非常重要,是ioc的初始化方法,其包含了,获得BeanDefinition的Resource定位,载入和注册。
接下来来分析refresh()方法,并重点了解BeanDefinition的Resource定位,
上源码:AbstractApplicationContext的refresh()方法,顺便打上注释。
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//重点在这
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory); //这是设置spring后置处理
this.invokeBeanFactoryPostProcessors(beanFactory); //调用后置处理器
this.registerBeanPostProcessors(beanFactory); //注册后置处理器
this.initMessageSource(); //初始化上下文
this.initApplicationEventMulticaster(); //初始化事件
this.onRefresh(); //初始化其他他叔的bean
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();
}
}
}
进入obtainFreshBeanFactory()方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
清晰可见调用了refreshBeanFactory()方法,这里refreshBeanFactory()调用了子类AbstractRefreshableApplicationContext的方法refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
可见,执行refreshBeanFactory()时,判断是否已经存在容器,如果存在,就销毁并且关闭容器,然后根据AbstractRefreshableApplicationContext父类容器创建了DefaultListableBeanFactory()容器。
再调用抽象方法loadBeanDefinitions(beanFactory) 这个方法是重点了,此方法由其子类实现
随便看看一个子类的实现,这里我挑了比较常见的子类XmlWebApplicationContext,贴源码
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//这里,我们有看到了上一篇文章的读取器(第三步),可见ioc容器基本离不开那4步,复习
//1.获得抽象资源,其中包含了BeanDefinition定义的信息。
//2.定义ioc容器。
//3.获得BeanDefinition读取器,这里获得抽象资源使用的是xml的方式,通过回调给factory容器
//4.通过读取器读取资源文件。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
对源码解释:beanDefinitionReader.setResourceLoader(this);
设置ResourceLoader,因为XmlWebApplicationContext是ResourceLoader的子类。
疑问: 为啥下面这个方法啥都没干,有没知晓的求告知。
this.initBeanDefinitionReader(beanDefinitionReader);
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
大头来了:this.loadBeanDefinitions(beanDefinitionReader);
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
String[] var3 = configLocations;
int var4 = configLocations.length;
for(int var5 = 0; var5 < var4; ++var5) {
String configLocation = var3[var5];
reader.loadBeanDefinitions(configLocation);
}
}
}
protected String[] getConfigLocations() {
return this.configLocations != null ? this.configLocations :
this.getDefaultConfigLocations();
}
这里的getConfigLocations()获取资源文件的路径,调用的是父类的方法,如果构造方法的String… locations 为空,那么就调用其子类的getDefaultConfigLocations()方法。
这里我挑选了子类XmlWebApplicationContext.
protected String[] getDefaultConfigLocations() {
return this.getNamespace() != null ? new String[]{"/WEB-INF/" + this.getNamespace() + ".xml"} : new String[]{"/WEB-INF/applicationContext.xml"};
}
可以从代码中看到默认的资源文件路径是/WEB-INF/applicationContext.xml。到了这里终于知道之前写了那么多的applicationContext.xml的用处了。
这里为止,完成了对资源文件的定位,上面那行代码就开始对BeanDefinition的载入工作,将放在下一篇博客继续了解,未完待续…