zoukankan      html  css  js  c++  java
  • 06.Spring 资源加载

    基本概念

    ResourceLoader 接口,在 Spring 中用于加载资源,通过它可以获取一个 Resouce 对象。


    内部构造

    首先来看它的接口定义:

    public interface ResourceLoader {
    
        // 从 classpath 加载资源时的前缀
        String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    
        // 关键-> 取得 Resource 对象,即获取资源
        Resource getResource(String location);
    
        ClassLoader getClassLoader();
    }

    再来看它的继承关系,如下所示:

    这里写图片描述

    • DefaultResourceLoader : 作为 ResourceLoader 接口的直接实现类,该类实现了基本的资源加载功能,可以实现对单个资源的加载。

    • ResourcePatternResolver :该接口继承了 ResourceLoader,定义了加载多个资源的方法, 可以实现对多个资源的加载。


    DefaultResourceLoader

    上面介绍过该类通过实现 ResourceLoader 接口实现了加载单个资源的功能。它的子类通过继承它来实现具体的资源访问策略。下面来探究下该类如何加载单个资源:

    public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
    
        // ① 是否以"/" 开头
        if (location.startsWith("/")) {
            return getResourceByPath(location);
        }
        // ② 是否以"classpath:" 开头,若是则表示该资源类型为 ClassPathResource
        else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        }
        else {
            try {
                // ③若都不是,则当成 UrlResource 来处理
                URL url = new URL(location);
                return new UrlResource(url);
            }catch (MalformedURLException ex) {
                // 若不是 UrlResource,则仍当作 ① 情况来处理 
                return getResourceByPath(location);
            }
        }
    }

    观察代码,发现在拿到资源的路径 loaction 后,会根据 location 的形式分成三种情况来处理:

    • 以 “/” 开头:默认调用类中的 getResourceByPath 方法处理。它的子类通过重写该方法实现不同形式的资源的访问。

    • 以 “classpath:” 开头:当成 ClassPathResource 资源对待。

    • 其他情况: 当成 UrlResource 资源对待,如果访问资源抛出异常,则调用 getResourceByPath 来完成资源的访问。


    ResourcePatternResolver

    该接口继承自 ResourceLoader 接口,在其基础上增加了同时对多个资源的访问。首先来看它的接口定义:

    public interface ResourcePatternResolver extends ResourceLoader {
    
        String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    
        // 例如使用 ant 风格的路径,匹配路径下的多个资源
        Resource[] getResources(String locationPattern) throws IOException;
    }

    1.PathMatchingResourcePatternResolver

    该类是 ResourcePatternResolver 接口的直接实现类,它是基于模式匹配的,默认使用AntPathMatcher 进行路径匹配,它除了支持 ResourceLoader 支持的前缀外,还额外支持 “classpath*” ,下面来探究下该类是如何实现对多个资源的访问。

    public Resource[] getResources(String locationPattern) throws IOException {
    
        Assert.notNull(locationPattern, "Location pattern must not be null");
    
        // 是否以 "classpath*:" 开头 
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
    
            // 通过 getPathMatcher 方法取得 PathMatcher ,默认只有 AntPathMatcher 一个实现类
            // 通过 isPattern 方法判断 "classpath*:" 之后的路径是否包含 "*" 或 "?"
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
    
                // 关键 -> 找到所有匹配路径(ant 风格)的资源
                return findPathMatchingResources(locationPattern);
    
            }else {
                // 关键 -> 通过类加载器查找
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }else {
    
            int prefixEnd = locationPattern.indexOf(":") + 1;
    
            // 判断资源路径 ":" 之后的部分是否包含 "*" 或 "?"
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                return findPathMatchingResources(locationPattern);
            }else {
                // 若不存在表示是单个资源,则通过从构造函数传入的 ResourceLoader 取得
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }

    观察代码:

    • 该方法首先会去判断资源路径是否是类路径下的资源(以 “classpath*:” 开头),然后再去判断路径是否允许存在多个匹配的资源(路径中包含 “*” 或 “?”)。

    • 只要资源路径允许多个匹配的资源,就会通过 findPathMatchingResources 方法寻找所有的匹配资源;

    • 若资源路径只存在单个匹配,则通过类加载器寻找类路径下的资源(findAllClassPathResources) ,其他资源则通过 ResourceLoader 的 getResource 方法获取。


    2.ApplicationContext

    通过上面的继承关系图可知,该接口继承了 ResourcePatternResolver 接口,说明它也集成了对对单个或多个资源的访问功能。

    当 Spring 需要进行资源访问时,实际上并不需要直接使用 Resource 实现类,而是调用 getResource 方法来获取资源。

    当通过 ApplicationContext 实例获取 Resource 实例时,它将会负责选择具体的 Resource 的实现类。代码如下:

    //通过 ApplicationContext访问资源
     Resource res = ctx.getResource("some/resource/path/myTemplate.txt);

    从上面代码中无法确定 Spring 将哪个实现类来访问指定资源,Spring 将采用和 ApplicationContext 相同的策略来访问资源。也就是说:如果 ApplicationContext 是 FileSystemXmlApplicationContext,res 就是 FileSystemResource 实例;如果 ApplicationContext 是 ClassPathXmlApplicationContext,res 就是 ClassPathResource 实例;如果 ApplicationContext 是 XmlWebApplicationContext,res 是 ServletContextResource 实例。

    也就是说 ApplicationContext 将会确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来,这就体现了策略模式的优势。


    参考

  • 相关阅读:
    python测试开发django(16)--admin后台中文版
    python测试开发django(15)--admin后台管理,python3.7与django3.06冲突,降低django为2.2
    python测试开发django(14)--JsonResponse返回中文编码问题
    python测试开发django(13)--查询结果转json(serializers)
    python测试开发django(12)--ORM查询表结果
    [二分,multiset] 2019 Multi-University Training Contest 10 Welcome Party
    [概率] HDU 2019 Multi-University Training Contest 10
    [dfs] HDU 2019 Multi-University Training Contest 10
    [bfs,深度记录] East Central North America Regional Contest 2016 (ECNA 2016) D Lost in Translation
    [状态压缩,折半搜索] 2019牛客暑期多校训练营(第九场)Knapsack Cryptosystem
  • 原文地址:https://www.cnblogs.com/moxiaotao/p/9349532.html
Copyright © 2011-2022 走看看