Spring通过ResourceLoader来处理得到的Resource。在前面我们知道容器初始化是以refresh()方法为入口的,内部的实现首先准备上下文,然后通过obtainFreshBeanFactory()方法获取beanFactory,
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
进入AbstractRefreshableApplicationContext类的refreshBeanFactory()方法:
protected final void refreshBeanFactory() throws BeansException {
//如果已经存在beanfactory则先销毁之后再重新创建
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//通过createBeanFactory()方法创建一个Ioc容器供容器使用,可以看到这个容器的类型是DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// 启动loadBeanDifinitions来载入BeanDifinition,因为有多种载入方式,这里通过一个抽象函数把具体的实现委托给子类来完成
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
BeanDefinition的具体载入过程委托给了AbstractRefreshableApplicationContext的子类完成,我们测试代码中实现该逻辑的子类就是AbstractXMLApplicationContext类,进入loadBeanDefinitions()方法:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为指定的BeanFactory创建一个XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 使用此上下文的资源加载环境配置BeanDefinitionReader,由于AbstartactXmlApplication继承了DefaultResourceLoader,所以这里的ResourceLoader传的是this
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader, then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
上面的loadBeanDefinitions方法中设置了ResourceLoader对象用来定位Resource,由于AbstartactXmlApplicationContext继承自DefaultResourceLoader,所以该类本身就是一个加载器。Spring设计的时候解耦的非常好,实际上BeanDefinition的定位,读入,注册过程是分开进行的。我们看下DefaultResourceLoader是如何实现资源定位的,
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
上面的getResourceByPath()方法会根据路径加载Resource对象
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
上面方法返回的是一个ClassPathContextResource对象,通过这个对象Spring就可以进行相关的I/O操作了。到这里我们就已经完成BeanDefinition的定位了。在BeanDefinition定位完成的基础上我们就可以通过返回的Resource对象进行BeanDefinition的载入了。