Spring 深入——IoC 容器 02( 二 )

loadBeanDefinitions() 进行进一步分析 。该方法调用的是本类的一个抽象方法loadBeanDefinitions(DefaultListableBeanFactory var1) , 此方法是模板方法,由子类具体实现:

Spring 深入——IoC 容器 02

文章插图
而 FSXAC 就是 AbstractXmlApplicationContext 的子类,所以进而分析这个类的具体实现 。
Spring 深入——IoC 容器 02

文章插图
可以看到:
  1. 创建了 XmlBeanDefinitionReader 类,用于将 XML 文件中的 Bean 读取出来并加载 。
  2. 调用 XBDR 的 loadBeanDefinitions,开始启动 BeanDefinition 的加载 。
  3. 在具体的实现中,分别传入不同的参数 , 但是在此方法中走判断时,调用了 this.getConfigResources() 这个方法在此类中是返回的 Resource[]是 null,所以走第二个判断,获取以字符串数组 , 因为之前在 FSXAC 中就设置好了 。
    Spring 深入——IoC 容器 02

    文章插图
String[] 传入调用的是 XmlBeanDefinitionReader 的基类 AbstractBeanDefinitionReader 的方法:
Spring 深入——IoC 容器 02

文章插图
这里就是将 String 数组中的字符串,一个一个传入调用本类重载方法,并且对其进行计数 。
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return this.loadBeanDefinitions(location, (Set)null);}public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {// 取得 ResourceLoader,使用的是 DeaultResourceLoaderResourceLoader resourceLoader = this.getResourceLoader(); // 关键代码1if (resourceLoader == null) {//...略} else {int loadCount;// 调用 DefaultResourceLoader 的 getResource 完成具体的 Resource 定位if (!(resourceLoader instanceof ResourcePatternResolver)) { // 关键代码2Resource resource = resourceLoader.getResource(location);loadCount = this.loadBeanDefinitions((Resource)resource);//... 略} else {// 调用 DefaultResourceLoader 的 getResources 完成具体的 Resource 定位try {Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);loadCount = this.loadBeanDefinitions(resources);//... 略}//... 略}}}可以看到最终调用的是两个参数的方法:(String location, Set<Resource> actualResources) , 通过上面代码的简要分析,我们提取出两个重要的信息:
  1. ResourceLoader 的作用?
  2. DefaultResourceLoader 的 getResource 完成了具体的 Resource 定位
首先第一个,Spring 将资源的定义和加载区分开来,这里需要注意的是资源的加载也就是 Resource 的加载,而不是 BeanDefinition 的加载 。Resource 定义了统一的资源(抽象并统一各种资源来源),ResourceLoader 定义了这些资源的统一加载 。所以 BeanDefinition 资源的定位过程应该是:将不同 BD 资源获取途径经过 Spring 统一封装为 Resource,再由 ResourceLoader 进行资源加载 , 获取这些 Resource,给 BeanDefinition 的载入做准备 。
而在这个 FSXAC 的例子中,这个 ResourceLoader 就是 DefaultResourceLoader,来看看是怎么具体实现 getResource().
public Resource getResource(String location) {Assert.notNull(location, "Location must not be null");Iterator var2 = this.protocolResolvers.iterator();Resource resource;do {if (!var2.hasNext()) {if (location.startsWith("/")) {// 处理以 / 标识的 Resource 定位return this.getResourceByPath(location);}// 处理带有 classpath 表示的 Resourceif (location.startsWith("classpath:")) {return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());}try {// 处理 URL 表示的 Resource 定位URL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException var5) {// 处理既不是 classpath 也不是 URL 标识的 Resource 定位// 则将 getResource 的责任交给 getResourceByPath(),这个方法时 protected,默认实现是得到一个 ClassPathContextResource 对象,通常会由子类实现该方法 。return this.getResourceByPath(location);}}ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();resource = protocolResolver.resolve(location, this);} while(resource == null);return resource;}通过上述分析,找到了熟悉的方法名: protected Resource getResourceByPath(String path){}
这个方法由子类 FSXAC 实现,这个方法返回的是:FileSystemResource 对象,通过这个对象,Spring 就可以进行相关的 I/O 操作,完成 BeanDefinition 定位 。
实际上这么多过程和细节,都是为了实现一个功能,对 path 进行解析,然后生成一个 FileSystemResource 对象,并返回,给 BeanDefinition 载入过程做准备 。

推荐阅读