Spring實作了自己的資源加載政策
- 職能劃分,資源的定義和資源的加載要有一個清晰的界限
- 統一的抽象,統一的資源定義和資源加載政策。
統一的資源:Resource
-
為 Spring 架構所有資源的抽象和通路接口,它繼承org.springframework.core.io.Resource
接口。作為所有資源的統一抽象,Resource 定義了一些通用的方法,由子類org.springframework.core.io.InputStreamSource
提供統一的預設實作AbstractResource
IoC之Spring統一資源加載政策 - 從上圖可以看到,Resource 根據資源的不同類型提供不同的具體實作,如下:
- FileSystemResource :對
類型資源的封裝,隻要是跟 File 打交道的,基本上與 FileSystemResource 也可以打交道。支援檔案和 URL 的形式,實作 WritableResource 接口,且從 Spring Framework 5.0 開始,FileSystemResource 使用 NIO2 API進行讀/寫互動。java.io.File
- ByteArrayResource :對位元組數組提供的資料的封裝。如果通過 InputStream 形式通路該類型的資源,該實作會根據位元組數組的資料構造一個相應的 ByteArrayInputStream。
- UrlResource :對
類型資源的封裝。内部委派 URL 進行具體的資源操作。java.net.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