Java 杂谈程序员ssh

说说在 Spring 框架中如何访问资源(文件、URL、二进制数

2018-04-18  本文已影响51人  deniro

1 资源抽象接口(Resource)

JDK 所提供的访问资源的类(如 java.net.URL、File 等)并不能很好的满足各种底层资源的访问需求,比如缺少从类路径或者 Web 容器的上下文中获取资源的操作类 。 为此, Spring 设计了一个 Resource 接口,它为应用程序提供了更强的访问底层资源的能力 。

Resource 接口的主要方法:

方法 说明
boolean exists() 是否存在
boolean isOpen() 是否打开
URL getURL() throws IOException 如果底层资源可以表示成 URL ,则返回对应的 URL 对象
File getFile() throws IOException 如果底层资源对应一个文件,则返回对应的 File 对象
InputStream getInputStream() throws IOException 返回资源对应的输入流
Resource 接口及实现类之间的关系
接口或类 说明
WritableResource 可写资源接口(Spring 3.1 + 新增),有两个实现类:FileSystemResource 和 PathResource(Spring 4.0 + 新增)
ByteArrayResource 二进制数组表示的资源,二进制数组资源可以在内存中通过程序构造。
ClassPathResource 类路径下的资源,资源以相对于类路径的方式表示。
FileSystemResource 文件系统资源,资源以文件系统路径的方式表示,如 D:/config.xml。
InputStreamResource 以输入流返回表示的资源。
ServletContextResource 以相对于 Web 应用根目录的路径下加载资源。支持以流和 URL 的方式访问资源;在 WAR 被解压的情况下,也可以通过 File 方式访问资源;还可以直接从 JAR 包中访问资源。
UrlResource Url 封装了 ava.net.URL,它能够访问如文件系统的资源、HTTP 以及 FTP 等资源。
PathResource Path 封装了 java.net.URL、java.nio.file.Path(Java 7.0 +)和文件系统资源,通过它可以访问 URL、Path 和系统文件路径表示的资源。

有了这个抽象的资源类之后,就可以将 Spring 的配置信息放置在任意地方咯O(∩_∩)O哈哈~


Spring 的 Resource 接口及其实现类,可以在脱离 Spring 框架的情况下实现,它与 JDK 提供的资源访问方式相比,更强大,也更好用。


假设需要访问 Web 应用类路径下的一个文件,那么我们可以使用以下这些方法:

public class FileSourceTest {

    public static void main(String[] args) throws IOException {
        String filePath="F:\\temp\\config.xml";

        //以系统文件路径的方式加载资源
        WritableResource writableResource=new PathResource(filePath);

        //写入
        OutputStream outputStream=writableResource.getOutputStream();
        outputStream.write("今天过得还好吗?".getBytes());
        outputStream.close();

        //读取
        InputStream inputStream=writableResource.getInputStream();
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        int i;
        while ((i=inputStream.read())!=-1){
            byteArrayOutputStream.write(i);
        }
        System.out.println("byteArrayOutputStream:"+byteArrayOutputStream.toString());
        System.out.println("writableResource-getFilename:"+writableResource.getFilename());

        //使用类路径的方式加载资源
        Resource resource=new ClassPathResource("resources/spring-ioc.xml");
        System.out.println("resource-getFilename:"+resource.getFilename());

    }
}

Resource 接口定义一些方法,用于访问文件的信息与数据:

方法 说明
getFileName() 获取文件名
getFile() 获取资源对应的 File 对象
getInputStream() 获取文件的输入流

WritableResource 接口定义一些方法,用于向文件写入数据:

方法 说明
getOutputStream() 获取文件的输出流
createRelative(String relativePath) 在相对地址上创建新文件

在 Web 应用中,可以通过 ServletContextResource 以相对于 Web 应用根目录的路径,加载文件资源:

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<jsp:directive.page import="org.springframework.web.context.support.ServletContextResource"/>
<jsp:directive.page import="org.springframework.core.io.Resource"/>
<jsp:directive.page import="org.springframework.web.util.WebUtils"/>

<%
    Resource resource=new
            ServletContextResource(application,"WEB-INF/resources/config.properties");//相对于 Web 应用的根路径
    out.print(resource.getFilename()+"<br/>");
    out.print(WebUtils.getTempDir(application).getAbsoluteFile());
%>

对于位于远程服务器(Web 服务器或 FTP 服务器)上的文件资源,可以使用 UrlResource 进行访问。

加载资源默认采用系统编码来读取内容。所以如果资源文件的内容采用的是特殊的编码格式,那么可以使用 EncodedResource 对资源进行编码:

EncodedResource encodedResource=new EncodedResource(resource,"UTF-8");
System.out.println(FileCopyUtils.copyToString(encodedResource.getReader()));

2 通过特殊标识加载资源

Spring 提供了一个强大的加载资源的机制,可以通过 "classpath:"、"file:" 等资源地址前缀识别不同的资源类型,还支持 Ant 风格的带通配符的资源地址 。

2.1 资源地址表达式

地址前缀 说明 示例
classpath: 从类路径中加载资源, classpath: 和 classpath:/ 是等价的,都是相对于类的根路径 。 资源文件可以在标准文件系统中,也可以在 jar 或者 zip 文件中 。 classpath:com/xxx/config.properties
file: 使用 UrlResource 从文件系统目录中装载资源,可以采用绝对或者相对路径 。 file:com/xxx/config.properties
http:// 使用 UrlResource 从 Web 服务器中装载资源。 http://www.xxx.com/xxx/config.properties
ftp:// 使用 UrlResource 从 FTP 服务器中装载资源。 ftp://www.xxx.com/xxx/config.properties
没有前缀 根据 ApplicationContext 具体实现类采用对应类型的 Resource。 com/xxx/config.properties

"classpath*:" 前缀与 “classpath:” 前缀的区别如下:

假设有这样的一个场景,假设有在 JAR 包或文件系统类路径下,存在多个同名的包名(比如 com.deniro)——

这种设计对于分模块打包的应用场景下很有用。假设一个名为 note 的应用,分为 2 个模块,每个模块对应一个配置文件(module1.xml 与 module2.xml ),都放置在 com.note 目录下,每个模块单独打 JAR 包。使用 classpath*:com/note/module*.xml 就可以同时加载这些模块中的配置文件啦O(∩_∩)O哈哈~

Ant 风格的资源地址支持以下 3 种匹配符:

匹配符 说明
? 匹配文件名中的一个字符。
* 匹配文件名中的任意个字符 。
** 匹配多层路径。

2.2 资源加载器

Spring 定义了加载资源的接口以及实现类:

接口或类 说明
ResourceLoader getResource(String location) 方法,可以根据地址加载资源,仅支持带资源类型前缀的表达式。
ResourcePatternResolver 扩展了 ResourceLoader 接口,定义的 getResources(String locationPattern) 方法,支持带资源类型前缀及 Ant 风格的资源路径表达式。
PathMatchingResourcePatternResolver 是 ResourcePatternResolver 的实现类。
public void getResources() throws IOException {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource[] resources = resolver.getResources("classpath*:net/deniro/**/*.class");
    Assert.assertNotNull(resources);

    for (Resource resource : resources) {
        System.out.println(resource.getDescription());
    }
}

在项目发布时,如果资源配置文件会被打包到 JAR 中,如果使用 Resource#getFile() 方法,会抛出 FileNotFoundException,我们可以使用 Resource#getInputStream() 来读取它。建议在实践中,尽量采用流的方式来读取配置文件,因为它总是有效的哦 O(∩_∩)O哈哈~


上一篇下一篇

猜你喜欢

热点阅读