资源加载器抽象和缺省实现

java (小工具) (Spring) Resource + ResourceLoader + DefaultResourceLoader(六)_开发语言


文章目录

1.ResourceLoader + DefaultResourceLoader 概述

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

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

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

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

2.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();

}

3.缺省实现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

3.总结

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

  1. 用户为​​DefaultResourceLoader​​提供自定义的​​ProtocolResolver​​用来识别特定格式的资源路径
    使用方法​​DefaultResourceLoader#addProtocolResolver​​添加​​ProtocolResolver​​,可以提供多个,也可以不提供。
    缺省情况下,​​DefaultResourceLoade​​r不包含​​ProtocolResolver​​。
  2. 给定一个路径,调用方法​​DefaultResourceLoader#getResource​​获取资源对象(也可以叫做句柄)
    2.1 轮询每个​​ProtocolResolver​​看它们哪个可以处理该路径,如果可以让其处理并返回相应的资源句柄;
    2.2 如果路径以"/"开头,尝试将其处理为一个​​ClassPathContextResource​​句柄并返回;
    2.3 如果路径以"classpath:“前缀开头,去除路径中前缀部分之后将其封装成一个​​ClassPathResource​​句柄并返回;
    2.4 如果路径是URL格式
    2.5 如果路径以"file:”/“vfs:”/"vfsfile:"前缀开头,将其封装成一个FileUrlResource句柄并返回;
    2.6 否则将其封装成一个​​UrlResource​​句柄返回;
    2.7 其他格式,仍然尝试将其封装一个​​ClassPathContextResource​​句柄并返回;

4.Spring资源抽象Resource

Spring对各种底层资源,比如文件系统中的一个文件,​​classpath​​​上的一个文件,或者一个​​网络URL​​​,统一抽象为接口​​Resource​​来表示

因为每个底层文件都可以以一个​​只读InputStream​​​的方式打开,所以​​Resource接口​​​继承自接口​​InputStreamSource​​​。​​InputStreamSource​​​接口中定义了方法​​getInputStream()​​用于返回打开该资源所对应的输入流。

java (小工具) (Spring) Resource + ResourceLoader + DefaultResourceLoader(六)_开发语言_02

java (小工具) (Spring) Resource + ResourceLoader + DefaultResourceLoader(六)_java_03

实现类

介绍

ClassPathResource

Java类路径上的一个资源文件,比如一个class文件,xml/properties配置文件,亦或一个图片文件等等

FileSystemResource

操作系统文件系统中的一个资源文件一个xml/properties配置文件,一个图片,一个pdf,一个jar包等等

UrlResource

将一个网络URL地址封装成一个资源文件一个静态图片,html,js,css,或者能动态生成json/xml

ByteArrayResource

将一个字节数组封装成一个Resource

ServletContextResource

获取ServletContext环境下的资源文件,在web应用程序的根目录下解释相对路径

InputStreamResource

将一个输入流封装成一个资源,因为用于描述一个已经打开的资源,所以isOpen()总是返回true

VfsResource

针对JBoss VFS 的资源

package test.resources;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.core.io.*;

import java.io.FileInputStream;
import java.io.InputStream;

@Slf4j
public class SpringResourceDemo {
/**
* 描述一个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 {
{
Resource resource = new ClassPathResource("test/resources/SpringResourceDemo.class");
describe(resource);
}

{// test.txt 是当前磁盘卷根目录下一个存在的文件,内容是:Test Spring Resource
Resource resource = new FileSystemResource("/test.txt");
describe(resource);
}

{//
Resource resource = new UrlResource("https://docs.spring.io");
describe(resource);
}

{//
Resource resource = new ByteArrayResource("Hello".getBytes());
describe(resource);
}


{// test.txt 是当前磁盘卷根目录下一个存在的文件,内容是:Test Spring Resource
InputStream is = new FileInputStream("/test.txt");
Resource resource = new InputStreamResource(is);
describe(resource);
}
}
}
====================================
toString : class path resource [test/resources/SpringResourceDemo.class]
contentLength : 2838
exists : true
getDescription : class path resource [test/resources/SpringResourceDemo.class]
isReadable : true
isOpen : false
getFilename : SpringResourceDemo.class
isFile : true
getFile : D:\springboot-tut-zero\target\test-classes\test\resources\SpringResourceDemo.class
lastModified : 1542634668385
getURI : file:/D:/springboot-tut-zero/target/test-classes/test/resources/SpringResourceDemo.class
getURL : file:/D:/springboot-tut-zero/target/test-classes/test/resources/SpringResourceDemo.class
====================================
toString : file [D:\test.txt]
contentLength : 20
exists : true
getDescription : file [D:\test.txt]
isReadable : true
isOpen : false
getFilename : test.txt
isFile : true
getFile : \test.txt
lastModified : 1542633020067
getURI : file:/D:/test.txt
getURL : file:/D:/test.txt
====================================
toString : URL [https://docs.spring.io]
contentLength : -1
exists : true
getDescription : URL [https://docs.spring.io]
isReadable : true
isOpen : false
getFilename :
isFile : false
lastModified : 0
getURI : https://docs.spring.io
getURL : https://docs.spring.io
====================================
toString : Byte array resource [resource loaded from byte array]
contentLength : 5
exists : true
getDescription : Byte array resource [resource loaded from byte array]
isReadable : true
isOpen : false
getFilename : null
isFile : false
====================================
toString : InputStream resource [resource loaded through InputStream]
contentLength : 20
exists : true
getDescription : InputStream resource [resource loaded through InputStream]
isReadable : true
isOpen : true
getFilename : null
isFile : false