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


  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 载入过程做准备 。
实际上 Spring 针对不同类型的 Resource 都准备了对应的实现类 , 方便我们针对不同场景进行合适的使用 , 不同的 ApplicationContext 会对应生成其他的 Resource:ClassPathResource、ServletContextResource 等 , 而且 Resource 接口本身就是继承了 InputStreamSource (这个抽象类唯一的方法是返回一个 InputStream) , 定义了很多的 I/O 相关的操作 , 其实现类也主要是针对不同的资源类型做出合适的实现 。
小结:通过 FileSystemXmlApplicationContext 这个 AC 实现原理为例子 , 初步的了解了 Resource 定位的解决方案 , 就是通过调用 getResourceByPath() 方法 , 重写了父类 DefaultResourceLoader 的方法 , 最后得到了 FileSystemResource 这个类型的 Resource 的定位实现 。那么此时这个 Resource 的定位过程已经完成 , 为 BeanDefinition 的载入创造了 I/O 操作的条件 , 但是具体的数据还没开始读入 。读入就是 BeanDefinition 的载入和解析过程了 。
其实 Resource 就是统一了资源的定义 , 各种 BeanDefinition 定义的资源(File , URL , XML...)都统一抽象成 Resource , 所有实现类都需要实现相关的 I/O 操作 。

经验总结扩展阅读