BeanDefinition的存在形式有很多种,例如文件系统中的Bean定义文件或类路径中的Bean定义文件。这就意味需要不同的寻址方式在找到这些Bean定义文件。Resource定位指的是BeanDefinition的资源定位,就是找到这些Bean定义文件,并将这些信息抽象为统一的Resource对象,方便后面载入。
以编程的方式使用DefaultListableBeanFactory时,我们会手动定义一个Resource来定位容器使用BeanDefinition:ClassPathResource res = new ``ClassPathResource("bean.xml");
然后定义一个特定的读取器来读取这个资源,因为DefaultListableBeanFactory是一个纯粹的IoC容器,并没有自带帮我们定位资源和读取资源的功能,所以需要手动为其配置特定的读取器。所以这里就能看出使用ApplicationContext的优势所在,因为在ApplicationCo-ntext中,已经为我们提供了一系列加载不同Resource的读取器的实现,例如FileSystemXmlApplicationContext,ClassPathXmlApplicationContext以及XmlWebApplicationContext等,简单地从这些类的名字上分析,可以清楚地看到它们可以提供哪些不同的Resource读入功能。当然,使用DefaultListable-BeanFactory这种更底层的容器,能提高定制IoC容器的灵活性。下面分析FileSystemXmlApplicationContext的资源定位过程。
在上面的分析中,我们可以了解到FileSystemXmlApplicationContext的基本功能都在其基类中完成,它只是在构造函数中调用基类的refresh()
来实例化容器和定义getResourceByPath()
来定位BeanDefinition资源。所以可以知到getResourceByPath()
是该容器定位资源调用的最后的方法,查看该方法的调用栈,便可了解整个定位过程。
分析调用栈:
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//这里判断如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
/**
*创建实际存储BeanDefinition的DefaultListableBeanFactory,
*并调用loadBeanDefinitions(beanFactory)载入BeanDefinition信息
*/
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());
}

```java
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
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);
}
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
//对configResources进行载入解析和注册
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
//根据configLocations定位Resource
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
/**
* Load bean definitions from the specified resource location.
* <p>The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* @param location the resource location, to be loaded with the ResourceLoader
* (or ResourcePatternResolver) of this bean definition reader
* @param actualResources a Set to be filled with the actual Resource objects
* that have been resolved during the loading process. May be <code>null</code>
* to indicate that the caller is not interested in those Resource objects.
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #getResourceLoader()
* @see #loadBeanDefinitions(org.springframework.core.io.Resource)
* @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
*/
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//这里取得ResourceLoader,就是之前绑定的FileSystemXmlApplicationContext
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//这里对Resource的路径模式进行解析。
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//调用DefaultResourceLoader的getResource完成具体的Resource定位
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
//调用DefaultResourceLoader的getResource完成具体的Resource定位
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//这里处理带有classpath标识的Resource
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标识的Resource定位
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
//如果既不是classpath.也不是URL标识的Resource定位,则把getResource的
//重任交给getResourceByPath,这个方法是一个protected方法.默认的实现是得到
//一个ClassPathContextResource,这个方法常常会用子类来实现
return getResourceByPath(location);
}
}
}
//getResourceByPath的默认实现
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}