天天看點

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