天天看点

IoC之Spring统一资源加载策略

Spring实现了自己的资源加载策略

  • 职能划分,资源的定义和资源的加载要有一个清晰的界限
  • 统一的抽象,统一的资源定义和资源加载策略。

统一的资源:Resource

  • org.springframework.core.io.Resource

     为 Spring 框架所有资源的抽象和访问接口,它继承 

    org.springframework.core.io.InputStreamSource

    接口。作为所有资源的统一抽象,Resource 定义了一些通用的方法,由子类 

    AbstractResource

     提供统一的默认实现
    IoC之Spring统一资源加载策略
  • 从上图可以看到,Resource 根据资源的不同类型提供不同的具体实现,如下:
  • FileSystemResource :对 

    java.io.File

     类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道。支持文件和 URL 的形式,实现 WritableResource 接口,且从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互。
  • ByteArrayResource :对字节数组提供的数据的封装。如果通过 InputStream 形式访问该类型的资源,该实现会根据字节数组的数据构造一个相应的 ByteArrayInputStream。
  • UrlResource :对 

    java.net.URL

    类型资源的封装。内部委派 URL 进行具体的资源操作。
  • ClassPathResource :class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
  • InputStreamResource :将给定的 InputStream 作为一种资源的 Resource 的实现类

统一的资源加载:ResourceLoader

  • ResourceLoader提供了统一的抽象,具体的实现由相应的子类来负责实现,类结构图如下:
    IoC之Spring统一资源加载策略
  • ResourceLoader 中最核心的方法是:
    Resource getResource(String location);      
  • DefaultResourceLoader对getResource提供了默认实现,代码如下:
    @Override
    public Resource getResource(String location) {
       Assert.notNull(location, "Location must not be null");
    
       for (ProtocolResolver protocolResolver : this.protocolResolvers) {
          Resource resource = protocolResolver.resolve(location, this);
          if (resource != null) {
             return resource;
          }
       }
       // 如果是"/"开头, 那么就通过ClassPathContextResource 
       if (location.startsWith("/")) {
          return getResourceByPath(location);
       }
       else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
       //如果以classpath为开头,那么就通过ClassPathResource
          return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
       }
       //然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源
       else {
          try {
             // Try to parse the location as a URL...
             URL url = new URL(location);
             return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
          }
          catch (MalformedURLException ex) {
             // No URL -> resolve as resource path.
             return getResourceByPath(location);
          }
       }
    }      
    protected Resource getResourceByPath(String path) {
       return new ClassPathContextResource(path, getClassLoader());
    }      
    public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
       Assert.notNull(path, "Path must not be null");
       String pathToUse = StringUtils.cleanPath(path);
       if (pathToUse.startsWith("/")) {
          pathToUse = pathToUse.substring(1);
       }
       this.path = pathToUse;
       this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }      
    protected Resource getResourceByPath(String path) {
       return new ClassPathContextResource(path, getClassLoader());
    }      
  • ResourceLoader的getResource(String location)每次只能根据location返回一个Resource资源,当需要加载多个资源的时候,就可以使用ResourcePartternResolver,它支持根据指定的资源路径匹配模式每次返回多个Resource实例

小结:

  • Spring 提供了 Resource 和 ResourceLoader 来统一抽象整个资源及其定位。使得资源与资源的定位有了一个更加清晰的界限,并且提供了合适的 Default 类,使得自定义实现更加方便和清晰。
  • AbstractResource 为 Resource 的默认抽象实现,它对 Resource 接口做了一个统一的实现,子类继承该类后只需要覆盖相应的方法即可,同时对于自定义的 Resource 我们也是继承该类。
  • DefaultResourceLoader 同样也是 ResourceLoader 的默认实现,在自定 ResourceLoader 的时候我们除了可以继承该类外还可以实现 ProtocolResolver 接口来实现自定资源加载协议。
  • DefaultResourceLoader 每次只能返回单一的资源,所以 Spring 针对这个提供了另外一个接口 ResourcePatternResolver ,该接口提供了根据指定的 locationPattern 返回多个资源的策略。其子类 PathMatchingResourcePatternResolver 是一个集大成者的 ResourceLoader ,因为它即实现了 

    Resource getResource(String location)

     方法,也实现了 

    Resource[] getResources(String locationPattern)

     方法。

ps:该文为芋道源码学习笔记

转载于:https://my.oschina.net/u/4055223/blog/3098311