spring 源码分析--IOC容器初始化四
在第二节中我们销毁了老的bean,第三节中有创建了一个新的DefaultListableBeanFactory 类型的工厂,接着又创建了一个 XmlBeanDefinitionReade类型的reader,顾名思义,这个reader就是去读取我们的配置文件,然后解析,完成初始化,在这一节里,我们要完成的是定位配置文件。
1.2.2.1.1.3.1.1 loadBeanDefinitions(configLocations): 定义在 XmlBeanDefinitionReader
的抽象父类 -AbstractBeanDefinitionReader 中:
通过循环参数数组:调用函数 loadBeanDefinitions(locations[i]) ,在该函数中又直接调用: loadBeanDefinitions(location, null) ;
原型 为: loadBeanDefinitions(String location, Set actualResources) 定义如下:
====================================================================
// 这里得到当前定义的 ResourceLoader , 默认的我们使用 DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader() ;
// 如果没有找到我们需要的 ResourceLoader ,直接抛出异常
if (resourceLoader == null ) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available" );
}
/**
* 由 getResourceLoader() 函数可知 resourceLoader 为
* PathMatchingResourcePatternResolver 类型,而
* PathMatchingResourcePatternResolver 继承 ResourcePatternResolver
* 这里处理我们在定义位置时使用的各种 pattern, 需要 ResourcePatternResolver 来完成
*/
if (resourceLoader instanceof ResourcePatternResolver) {
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location) ;
/**
* 自此对资源文件的装载过程结束。下面是对文件的解析和初始化 bean 过程,下面的函数最终
* 会调用 XmlBeanDefinitionReader 类的 loadBeanDefinitions(EncodedResource
* encodedResource)
*/
int loadCount = loadBeanDefinitions(resources) ;
if (actualResources != null ) {
for ( int i = 0; i < resources. length ; i++) {
actualResources.add(resources[i]) ;
}
}
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.
// 这里通过 ResourceLoader 来完成位置定位
Resource resource = resourceLoader.getResource(location);
// 这里已经把一个位置定义转化为 Resource 接口,可以供 XmlBeanDefinitionReader 来使用了
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null ) {
actualResources.add(resource) ;
}
if ( logger .isDebugEnabled()) {
logger .debug( "Loaded " + loadCount + " bean definitions from location [" + location + "]" );
}
return loadCount;
}
====================================================================
描述:
1.2.2.1.1.3.1.1.1 getResourceLoader() :从类图中可以看出在 AbstractBeanDefinitionReader 中有 private ResourceLoader resourceLoader ;即是此处的 resourceLoader ;然后注意到 XmlBeanDefinitionReader 的构造函数:
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super (registry);
}
回到父类 AbstractBeanDefinitionReader 的构造函数:
====================================================================
Assert.notNull (registry, "BeanDefinitionRegistry must not be null" );
this . registry = registry;
// Determine ResourceLoader to use.
if ( this . registry instanceof ResourceLoader) {
this . resourceLoader = (ResourceLoader) this . registry ;
} else {
this . resourceLoader = new PathMatchingResourcePatternResolver ();
}
====================================================================
可以看出在构造函数中,如果传入的参数是一个 ResourceLoader 类型的对象,那么他将在类型转化后赋给 resourceLoader 。回到 XmlBeanDefinitionReader 创建的地方,即在类: AbstractXmlApplicationContext 的 loadBeanDefinitions 函数里。看到传入的参数是: beanFactory ,找到 beanFactory 的定义处 —AbstractRefreshableApplicationContext 的函数: refreshBeanFactory 里,为:
DefaultListableBeanFactory beanFactory = createBeanFactory() ;由类图中可以看出 beanFactory 不是一个 ResourceLoader 类型,所以执行:
this . resourceLoader = new PathMatchingResourcePatternResolver () ;在构造函数可以看见实际上是创建了一个 DefaultResourceLoader
返回。
1.2.2.1.1.3.1.1.2 getResources(location) :由上面的解释可以知道 resourceLoader 实际上是 PathMatchingResourcePatternResolver 类型,
所以在 PathMatchingResourcePatternResolver 类中的找到 getResources(String locationPattern) 方法如下:
====================================================================
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull (locationPattern, "Location pattern must not be null" );
// 如果 locationPattern 是以字符串: “ classpath*: ”开始。
if (locationPattern.startsWith( CLASSPATH_ALL_URL_PREFIX )) {
if (getPathMatcher().isPattern(locationPattern.substring( CLASSPATH_ALL_URL_PREFIX .length()))) {
// 一个 classpath 资源。
return findPathMatchingResources(locationPattern);
} else {
// 多个 classpath 资源
return findAllClassPathResources(locationPattern.substring( CLASSPATH_ALL_URL_PREFIX .length()));
}
} else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf( ":" ) + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// 文件类型
return findPathMatchingResources(locationPattern);
} else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern) };
}
}
}
====================================================================
我们关注最后一行,即红色部分,这一行的本质是去调用 DefaultResourceLoader 类的
Resource getResource(String location) 方法:方法如下:
====================================================================
public Resource getResource(String location) {
Assert .notNull (location, "Location must not be null" );
// 如果是类路径的方式,那需要使用 ClassPathResource 来得到 bean 文件的资源对象
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 方式,使用 UrlResource 作为 bean 文件的资源对象
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException ex) {
// 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
====================================================================
我们的 FileSystemXmlApplicationContext 本身就是是 DefaultResourceLoader 的实现类,他实现了以下的接口:
====================================================================
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith( "/" )) {
path = path.substring(1);
}
// 这里使用文件系统资源对象来定义 bean 文件
return new FileSystemResource (path);
}
====================================================================
这样代码就回到了 FileSystemXmlApplicationContext 中来,他提供了 FileSystemResource 来完成从文件系统得到配置文件的资源定义。这样,就可以从文件系统路径上对 IOC 配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在 Spring 中我们看到它提供的各种资源抽象,比如 ClassPathResource , URLResource ,FileSystemResource 等来供我们使用。上面我们看到的是定位 Resource 的一个过程,而这只是加载过程的一 部分 - 我们回到 AbstractBeanDefinitionReaderz 中的 loadDefinitions(resource) 来看看得到代表 bean 文 件的资源定义以后的载入过程 , 默认的我们使用 XmlBeanDefinitionReader返回
本站支持 pay for your wishes