Spring资源加载器抽象和缺省实现 -- ResourceLoader + DefaultResourceLoader

本文深入解析Spring框架中ResourceLoader接口及DefaultResourceLoader实现,展示如何加载不同类型的资源,包括类路径、文件系统和网络资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

对于每一个底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个以URL形式表示的网络资源,Spring 统一使用 Resource 接口进行了建模抽象,相应地,对于这些资源的加载,Spring使用了 ResourceLoader 进行了统一建模抽象。

通过ResourceLoader,给定其可以接受的资源路径,我们可以获得对应资源的Resource对象,然后进行进行相应的资源访问。

Spring提供了一个缺省的ResourceLoader实现DefaultResourceLoader。该实现类可以加载classpath或者文件系统中的某个文件,或者URL形式存在的某个网络资源。Spring的各种应用上下文都间接地通过基类AbstractApplicationContext继承自DefaultResourceLoader,所以这些应用上下文自身具备并且使用DefaultResourceLoader所定义资源加载能力。

本文我们先看一下接口ResourceLoader本身,然后通过一些例子看ResourceLoader/DefaultResourceLoader如果使用。

ResourceLoader接口定义

package org.springframework.core.io;

import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils;

public interface ResourceLoader {

	/** Pseudo URL prefix for loading from the class path: "classpath:". */
	// 从 classpath 上加载资源的伪URL前缀: classpath:
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;


	/**
	 * 给定资源路径,返回相应的资源Resource对象。所返回的Resource对象,
	 * 也可以叫做资源句柄,必须是可重用的资源描述符,允许多次在其上
	 * 调用方法Resource#getInputStream()。
	 * 另外 :
	 * 
	 * 1. 必须支持全路径URL : 比如 "file:C:/test.dat".
	 * 2. 必须支持classpath伪URL : 比如 "classpath:test.dat".
	 * 3. 应该支持相对文件路径:比如 "WEB-INF/test.dat". (实现相关)
	 * 
	 * 注意 : 所返回的资源句柄并不意味着对应的资源是已经存在的,所传入的参数
	 * 只是一个资源路径,并不代表相应的资源已经存在;使用者必须调用方法Resource#exists
	 * 来判断对应资源的存在性。
	 * @param location 资源路径
	 * @return 相应的资源句柄,总是不为null(哪怕对应的资源不存在)
	 */
	Resource getResource(String location);

	/**
	 * 暴露当前ResourceLoader所使用的ClassLoader给外部。
	 */
	@Nullable
	ClassLoader getClassLoader();

}

缺省实现DefaultResourceLoader的使用


@Slf4j
public class SpringDefaultResourceLoaderDemo {
    /**
     * 描述一个Resource实例
     *
     * @param resource
     * @throws Exception
     */
    void describe(Resource resource) throws Exception {
        log.info("====================================");
        log.info("toString : {}", resource.toString());
        log.info("contentLength : {}", resource.contentLength());
        log.info("exists : {}", resource.exists());
        log.info("getDescription : {}", resource.getDescription());
        log.info("isReadable : {}", resource.isReadable());
        log.info("isOpen : {}", resource.isOpen());
        log.info("getFilename : {}", resource.getFilename());
        log.info("isFile : {}", resource.isFile());
        if (resource.isFile()) {
            // getFile()仅针对文件类型Resource有效,可以是文件系统文件或者classpath上的文件
            log.info("getFile : {}", resource.getFile());
        }

        if (!((resource instanceof ByteArrayResource) || (resource instanceof InputStreamResource))) {
            // 以下三个属性针对 ByteArrayResource/InputStreamResource 类型资源无效,调用的话会抛出异常
            log.info("lastModified : {}", resource.lastModified());
            log.info("getURI : {}", resource.getURI());
            log.info("getURL : {}", resource.getURL());
        }
    }

    @Test
    public void test() throws Exception {
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        {// 获取 classpath 上的某个资源 : 路径带前缀 classpath:
            Resource resource = resourceLoader.getResource(
	            "classpath:test/resources/SpringDefaultResourceLoaderDemo.class");
            describe(resource);
        }

        {// 获取 classpath 上的某个资源 : 路径不带前缀 classpath:
            Resource resource = resourceLoader.getResource(
            	"test/resources/SpringDefaultResourceLoaderDemo.class");
            describe(resource);
        }

        {// 获取 classpath 上的某个资源 : 路径前缀为/,而不是 classpath:
            Resource resource = resourceLoader.getResource(
            	"/test/resources/SpringDefaultResourceLoaderDemo.class");
            describe(resource);
        }

        {// 获取网络上指定 url 的某个资源
            Resource resource = resourceLoader.getResource(
            	"https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference"
	            +"/pdf/spring-framework-reference.pdf");
            describe(resource);
        }

        {// 获取文件系统中的某个资源
            // test.txt 是当前磁盘卷根目录下一个存在的文件,内容是:Test Spring Resource
            Resource resource = resourceLoader.getResource("file:/test.txt");
            describe(resource);
        }
    }
}

上面的测试代码执行输出如下:

====================================
toString : class path resource [test/resources/SpringDefaultResourceLoaderDemo.class]
contentLength : 3011
exists : true
getDescription : class path resource [test/resources/SpringDefaultResourceLoaderDemo.class]
isReadable : true
isOpen : false
getFilename : SpringDefaultResourceLoaderDemo.class
isFile : true
getFile : D:\idea_wks\springboot-tut-zero\target\test-classes\test\resources\SpringDefaultResourceLoaderDemo.class
lastModified : 1546249104715
getURI : file:/D:/idea_wks/springboot-tut-zero/target/test-classes/test/resources/SpringDefaultResourceLoaderDemo.class
getURL : file:/D:/idea_wks/springboot-tut-zero/target/test-classes/test/resources/SpringDefaultResourceLoaderDemo.class
====================================
toString : class path resource [test/resources/SpringDefaultResourceLoaderDemo.class]
contentLength : 3011
exists : true
getDescription : class path resource [test/resources/SpringDefaultResourceLoaderDemo.class]
isReadable : true
isOpen : false
getFilename : SpringDefaultResourceLoaderDemo.class
isFile : true
getFile : D:\idea_wks\springboot-tut-zero\target\test-classes\test\resources\SpringDefaultResourceLoaderDemo.class
lastModified : 1546249104715
getURI : file:/D:/idea_wks/springboot-tut-zero/target/test-classes/test/resources/SpringDefaultResourceLoaderDemo.class
getURL : file:/D:/idea_wks/springboot-tut-zero/target/test-classes/test/resources/SpringDefaultResourceLoaderDemo.class
====================================
toString : class path resource [test/resources/SpringDefaultResourceLoaderDemo.class]
contentLength : 3011
exists : true
getDescription : class path resource [test/resources/SpringDefaultResourceLoaderDemo.class]
isReadable : true
isOpen : false
getFilename : SpringDefaultResourceLoaderDemo.class
isFile : true
getFile : D:\idea_wks\springboot-tut-zero\target\test-classes\test\resources\SpringDefaultResourceLoaderDemo.class
lastModified : 1546249104715
getURI : file:/D:/idea_wks/springboot-tut-zero/target/test-classes/test/resources/SpringDefaultResourceLoaderDemo.class
getURL : file:/D:/idea_wks/springboot-tut-zero/target/test-classes/test/resources/SpringDefaultResourceLoaderDemo.class
====================================
toString : URL [https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/pdf/spring-framework-reference.pdf]
contentLength : 5762367
exists : true
getDescription : URL [https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/pdf/spring-framework-reference.pdf]
isReadable : true
isOpen : false
getFilename : spring-framework-reference.pdf
isFile : false
lastModified : 1496863570000
getURI : https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/pdf/spring-framework-reference.pdf
getURL : https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/pdf/spring-framework-reference.pdf
====================================
toString : URL [file:/test.txt]
contentLength : 20
exists : true
getDescription : URL [file:/test.txt]
isReadable : true
isOpen : false
getFilename : test.txt
isFile : true
getFile : \test.txt
lastModified : 1542633020067
getURI : file:/test.txt
getURL : file:/test.txt

总结

上面的例子演示了DefaultResourceLoader的使用方法和效果,实际上,DefaultResourceLoader使用方法可以总结如下:

  1. 用户为DefaultResourceLoader提供自定义的ProtocolResolver用来识别特定格式的资源路径,
  1. 使用方法DefaultResourceLoader#addProtocolResolver添加ProtocolResolver,可以提供多个,也可以不提供。
  2. 缺省情况下,DefaultResourceLoader不包含ProtocolResolver
  1. 给定一个路径,调用方法DefaultResourceLoader#getResource获取资源对象(也可以叫做句柄)

DefaultResourceLoader会以以下顺序识别路径 :

  1. 轮询每个ProtocolResolver看它们哪个可以处理该路径,如果可以让其处理并返回相应的资源句柄;
  2. 如果路径以"/"开头,尝试将其处理为一个ClassPathContextResource句柄并返回;
  3. 如果路径以"classpath:"前缀开头,去除路径中前缀部分之后将其封装成一个ClassPathResource句柄并返回;
  4. 如果路径是URL格式
    1. 如果路径以"file:"/“vfs:”/"vfsfile:"前缀开头,将其封装成一个FileUrlResource句柄并返回;
    2. 否则将其封装成一个UrlResource句柄返回;
  5. 其他格式,仍然尝试将其封装一个ClassPathContextResource句柄并返回;

上面2,3,5方式其实都是将路径认为是classpath资源并返回相应的句柄,对同一个classpath资源路径,以下三种路径
分别对应上面的2,3,5三种情况 :
2. classpath:test/resources/SpringDefaultResourceLoaderDemo.class
3. /test/resources/SpringDefaultResourceLoaderDemo.class
5. test/resources/SpringDefaultResourceLoaderDemo.class

参考资源

Spring资源抽象Resource

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值