Spring-資源加載

在 Java 中,將不一樣來源的資源抽象成 URL ,經過註冊不一樣的 handler ( URLStreamHandler ) 來處理不一樣來源的資源的讀取邏輯。java

然而 URL 沒有默認定義相對 Classpath 或 ServletContext 等資源的 handler ,雖然能夠註冊本身的 URLStreamHandler 來解析特定的 URL 前綴(協議)。可是 URL 也沒有提供基本的方法、如檢查當前資源是否存在,檢查資源是否存在等方法。面試

URL: 我能夠加載各類的資源.......XXX

Spring: 你是個好人spring

Spring 實現了本身的資源加載策略設計模式

  • 職能劃分清楚。資源的定義和資源的加載有明確的界限
  • 統一的抽象,統一的資源定義和資源加載策略。

統一資源

org.springframework.core.io.Resource 接口抽象了全部 Spring 內部使用到的底層資源,如 File、URL、Classpath。數組

public interface Resource extends InputStreamSource {
    .......
    .......
}

org.springframework.core.io.InputStreamSource 封裝任何能返回 InputStream 的類、如 File、Classpath 下的資源和 ByteArray,該接口只有一個方法 getInputStream網絡

public interface InputStreamSource {

    /**
     * 表示任意形式的資源均可以被轉換成輸入流、最好每一次調用這個方法的時候都是返回一個新的InputStream
     * 看子類{@link FileSystemResource}符合這個要求
     */
    InputStream getInputStream() throws IOException;

}

Resource 接口提供了比 URL 更加多和便捷的方法app

idea 截圖

對於不一樣來源的資源文件都有相應的 Resource 實現框架

  • 文件 FileSystemResource
  • classpath 資源 ClassPathResource
  • url 資源 URLResource
  • InputStream 資源 InputStreamResource
  • byte 數組資源 ByteArrayResource

idea 類圖

其中 AbstractResource 實現了 Resource 接口中大多數的方法,若是咱們要自定義 Resource ,能夠繼承這個類,而不是直接實現 Resource ide

idea 截圖

統一資源加載

org.springframework.core.io.ResourceLoader 是 Spring 資源加載的抽象this

public interface ResourceLoader {
    /**
     * Pseudo URL prefix for loading from the class path: "classpath:".
     */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    /**
     * 根據 資源路徑 返回一個Resource 句柄、但不確保這個Resource 必定存在、須要調用 Resource exists 方法                 * 判斷
     * URL位置資源,如」file:C:/test.dat」
     * ClassPath位置資源,如」classpath:test.dat」
     * 相對路徑資源,如」WEB-INF/test.dat」,此時返回的Resource實例根據實現不一樣而不一樣
     *
     * 要可重用、可屢次調用 getInputStream
     */
    Resource getResource(String location);
    @Nullable
    ClassLoader getClassLoader();

}

idea 類圖

主要實現類就是 DefaultResourceLoader ,其中最核心的方法就是 getResource

@Override
        public Resource getResource(String location) {
            Assert.notNull(location, "Location must not be null");

            // 協議解釋
            for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
                Resource resource = protocolResolver.resolve(location, this);
                if (resource != null) {
                    return resource;
                }
            }

        // 若是以/ 開頭、則建立 ClassPathContextResource、其實也是一個 ClassPathResource 是他的子類、 類路徑的資源
        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());
        } 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);
            }
        }
    }

其中 ProtocolResolver

@FunctionalInterface
public interface ProtocolResolver {

    @Nullable
    Resource resolve(String location, ResourceLoader resourceLoader);
}

主要用於處理用戶自定義資源協議的,咱們能夠經過實現此接口來解釋咱們自定義的資源協議,只須要將實現類對象添加到 DefaultResourceLoader

public void addProtocolResolver(ProtocolResolver resolver) {
        Assert.notNull(resolver, "ProtocolResolver must not be null");
        this.protocolResolvers.add(resolver);
    }

ResourcePatternResolver

在 ResourceLoader 接口的基礎上增長了 Resource[] getResources(String locationPattern) 方法,支持根據指定的路徑匹配模式每次返回多個 Resource 實例。

@Override
    public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");
        // classpath*:
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            // a class path resource (multiple resources for same name possible)
            // 路徑中包含通配符
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                // a class path resource pattern
                return findPathMatchingResources(locationPattern);
            }
            else {
                // all class path resources with the given name
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }
        else {
            // Generally only look for a pattern after a prefix here,
            // and on Tomcat only after the "*/" separator for its "war:" protocol.
            int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                    locationPattern.indexOf(':') + 1);
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                // a file pattern
                return findPathMatchingResources(locationPattern);
            }
            else {
                // a single resource with the given name
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }

涉及到的設計模式

面試的時候

面試官: 看過什麼框架的源碼嗎

菜雞的我: 我看過 Spring 的源碼

面試官: 那你說說 Spring 中用到了什麼設計模式

菜雞的我: 額....好像...有....我忘記了

責任鏈模式

在資源定義和資源加載這一塊中,咱們能夠看到在 DefaultResourceLoader 中的 ProtocolResolver,

private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);

public Resource getResource(String location) {
            Assert.notNull(location, "Location must not be null");

            // 協議解釋
            for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
                Resource resource = protocolResolver.resolve(location, this);
                if (resource != null) {
                    return resource;
                }
            }
      .....
      .....

一個加載資源的請求,會被其中一個 ProtocolResolver 去解釋成一個 Resource 或者沒有一個 ProtocolResolver 能處理這個請求並返回一個 Resource (最終被後面的默認解析成Resource)。這一個過程其實算是使用到了責任鏈模式的,只是否是一個純正的責任鏈模式。

圖片來源於網絡

閻宏博士的《JAVA與模式》一書中是這樣描述責任鏈(Chain of Responsibility)模式的

責任鏈模式是一種對象的行爲模式。在責任鏈模式裏,不少對象由每個對象對其下家的引用而鏈接起來造成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪個對象最終處理這個請求,這使得系統能夠在不影響客戶端的狀況下動態地從新組織和分配責任。

純與不純的責任鏈模式

  • 純的責任鏈模式: 要麼處理這個請求、要麼徹底不處理這個請求並將這個請求傳遞給下一個處理器;不純的責任鏈模式:某個處理器處理了一部分這個請求的業務,而後又將剩下的傳遞給下一個處理器讓其繼續處理
  • 純的責任鏈模式: 一個請求必須被一個請求處理器所處理;不純的責任鏈模式:這個請求可能沒有被任何一個處理器處理

而對於怎麼存儲下一個 handler 的引用,固然能夠在當前 handler 中存有下一個 handler 的引用,可是更加經常使用的仍是使用數組或者列表將全部 handler 存儲起來,而後進行鏈式調用處理請求,如 servlet 的filter即便如此。

策略模式

不一樣的 ResourceLoader 對應着不一樣的資源加載策略

idea 類圖

org.springframework.context.support.GenericApplicationContext 類中

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    private final DefaultListableBeanFactory beanFactory;

    @Nullable
    private ResourceLoader resourceLoader;
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public Resource getResource(String location) {
        if (this.resourceLoader != null) {
            return this.resourceLoader.getResource(location);
        }
    // 最終調用 DefaultResourceLoader 的 getResource
        return super.getResource(location);
    }

這個不就是策略模式嗎

圖片來源自維基百科

相關文章

Spring 專輯

此次必定?

相關文章
相關標籤/搜索