Spring Resources 资源操作

本文详细介绍了Spring框架中的Resource接口及其多种实现类,包括UrlResource、ClassPathResource等,并展示了如何通过ResourceLoader访问资源。

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

一、概述

        Java 的标准 java.net.URL 类和各种 URL 前缀的标准处理程序无法满足所有对 low-level 资源的访问,比如:没有标准化的 URL 实现可用于访问需要从类路径或相对于 ServletContext 获取的资源。并且缺少某些 Spring 所需要的功能,例如检测某资源是否存在等。而 Spring Resource 声明了访问 low-level 资源的能力。

二、Resource 接口

        Spring Resource 接口位于 org.springframework.core.io 中。 旨在成为一个更强大的接口,用于抽象对低级资源的访问。以下显示了 Resource 接口定义的方法

public interface Resource extends InputStreamSource {

    // 返回一个布尔值,表明某个资源是否以物理形式存在
    boolean exists();

    // 表明资源的目录读取是否通过 getInputStream() 进行读取。
    default boolean isReadable() {
        return this.exists();
    }

    // 返回一个布尔值,指示此资源是否具有开放流的句柄。如果为 true,InputStream 就不能够多次读取,只能够读取一次并且及时关闭以避免内存泄漏。
    // 对于所有常规资源实现,返回 false,但是 InputStreamResource 除外。
    default boolean isOpen() {
        return false;
    }
    
    // 表明这个资源是否代表了一个文件系统的文件。
    default boolean isFile() {
        return false;
    }

    //  返回一个 URL 句柄,如果资源不能够被解析为 URL,将抛出 IOException
    URL getURL() throws IOException;

    // 返回一个资源的 URI 句柄
    URI getURI() throws IOException;

    // 返回某个文件,如果资源不能够被解析称为绝对路径,将会抛出 FileNotFoundException
    File getFile() throws IOException;

    default ReadableByteChannel readableChannel() throws IOException {
        return Channels.newChannel(this.getInputStream());
    }

    default byte[] getContentAsByteArray() throws IOException {
        return FileCopyUtils.copyToByteArray(this.getInputStream());
    }

    default String getContentAsString(Charset charset) throws IOException {
        return FileCopyUtils.copyToString(new InputStreamReader(this.getInputStream(), charset));
    }

    long contentLength() throws IOException;

    // 资源最后一次修改的时间戳
    long lastModified() throws IOException;
    
    // 创建此资源的相关资源
    Resource createRelative(String relativePath) throws IOException;

    // 资源的文件名是什么 例如:最后一部分的文件名 myfile.txt
    @Nullable
    String getFilename();

    // 返回资源的描述,用来输出错误的日志。这通常是完全限定的文件名或资源的实际 URL。
    String getDescription();
}

        Resource 接口继承了 InputStreamSource 接口,提供了很多 InputStreamSource 所没有的方法。InputStreamSource 接口,只有一个方法:

public interface InputStreamSource {
    // 找到并打开资源,返回一个 InputStream 以从资源中读取。预计每次调用都会返回一个新的 InputStream(),调用者有责任关闭每个流
    InputStream getInputStream() throws IOException;
}

三、Resource 的实现类

        Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。Resource 一般包括这些实现类:UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResource

3.1 UrlResource

        UrlResource  Resource 的一个实现类,用来访问网络资源,它支持 URL 的绝对路径。

http:       // 该前缀用于访问基于 HTTP 协议的网络资源。

ftp:        // 该前缀用于访问基于 FTP 协议的网络资源

file:       //  该前缀用于从文件系统中读取资源

3.1.1 访问 http 协议网络资源

import org.springframework.core.io.UrlResource;

public class UrlResourceDemo {

    public static void loadAndReadUrlResource(String path){
        // 创建一个 Resource 对象
        UrlResource url = null;
        try {
            url = new UrlResource(path);
            // 获取资源名
            System.out.println(url.getFilename());
            System.out.println(url.getURI());
            // 获取资源描述
            System.out.println(url.getDescription());
            // 获取资源内容
            System.out.println(url.getInputStream().read());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void main(String[] args) {
        //访问网络资源
        loadAndReadUrlResource("http://www.baidu.com");
    }
}

3.1.2 从文件系统访问资源

        在项目根路径下创建文件,从文件系统中读取资源

public static void main(String[] args) {
	// 访问文件系统资源
	loadAndReadUrlResource("file:resource.txt");
}

3.2 ClassPathResource

        ClassPathResource 用来访问类加载路径下的资源,相对于其他的 Resource 实现类,其主要优势是方便访问类加载路径里的资源,尤其对于 Web 应用,ClassPathResource 可自动搜索位于 classes 下的资源文件,无须使用绝对路径访问。

        在类路径下创建文件 resource.txt,使用 ClassPathResource 访问,如下:

import org.springframework.core.io.ClassPathResource;
import java.io.InputStream;

public class ClassPathResourceDemo {

    public static void loadAndReadUrlResource(String path) throws Exception{
        // 创建一个 Resource 对象
        ClassPathResource resource = new ClassPathResource(path);
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        // 获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
            System.out.println(new String(b));
        }
    }
    public static void main(String[] args) throws Exception {
        loadAndReadUrlResource("resource.txt");
    }
}

        ClassPathResource 实例可使用 ClassPathResource 构造器显式地创建,但更多的时候它都是隐式地创建的。当执行 Spring 的某个方法时,该方法接受一个代表资源路径的字符串参数,当 Spring 识别该字符串参数中包含 classpath: 前缀后,系统会自动创建 ClassPathResource 对象。

3.3 FileSystemResource

        Spring 提供的 FileSystemResource 类用于访问文件系统资源,使用 FileSystemResource 来访问文件系统资源并没有太大的优势,因为 Java 提供的 File 类也可用于访问文件系统资源。

import org.springframework.core.io.FileSystemResource;

import java.io.InputStream;

public class FileSystemResourceDemo {

    public static void loadAndReadUrlResource(String path) throws Exception{
        // 相对路径
        // FileSystemResource resource = new FileSystemResource(path);
        // 绝对路径
        FileSystemResource resource = new FileSystemResource("C:\\resource.txt");
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
            System.out.println(new String(b));
        }
    }
    public static void main(String[] args) throws Exception {
        loadAndReadUrlResource("resource.txt");
    }
}

        FileSystemResource 实例可使用 FileSystemResource 构造器显示地创建,但更多的时候它都是隐式创建。执行 Spring 的某个方法时,该方法接受一个代表资源路径的字符串参数,当 Spring 识别该字符串参数中包含 file: 前缀后,系统将会自动创建 FileSystemResource 对象。

3.4 ServletContextResource

        这是 ServletContext 资源的 Resource 实现,它解释相关 Web 应用程序根目录中的相对路径。它始终支持流 (stream) 访问和 URL 访问,但只有在扩展 Web 应用程序存档且资源实际位于文件系统上时才允许 java.io.File 访问。无论它是在文件系统上扩展还是直接从 JAR 或其他地方(如数据库)访问,实际上都依赖于 Servlet 容器。

3.5 InputStreamResource

        InputStreamResource 是给定的输入流 (InputStream) 的 Resource 实现。它的使用场景在没有特定的资源实现的时候使用 (感觉和 @Component 的适用场景很相似)。与其他 Resource 实现相比,这是已打开资源的描述符。 因此,它的 isOpen() 方法返回 true。如果需要将资源描述符保留在某处或者需要多次读取流,请不要使用它。

3.6 ByteArrayResource

        字节数组的 Resource 实现类。通过给定的数组创建了一个 ByteArrayInputStream。它对于从任何给定的字节数组加载内容非常有用,而无需求助于单次使用的 InputStreamResource

四、Resource 类图

        上述 Resource 实现类与 Resource 顶级接口之间的关系可以用下面的 UML 关系模型来表示

五、ResourceLoader 接口

5.1 概述

        Spring 提供如下两个标志性接口,如下:

1、ResourceLoader :该接口实现类的实例可以获得一个 Resource 实例。

2、ResourceLoaderAware :该接口实现类的实例将获得一个 ResourceLoader 的引用。

        在 ResourceLoader 接口里有如下方法,该接口仅有这个方法,用于返回一个 Resource 实例。ApplicationContext 实现类都实现 ResourceLoader 接口,因此 ApplicationContext 可直接获取 Resource 实例。

Resource getResource(String location)

5.2 使用示例

        1、使用 ClassPathXmlApplicationContext 获取 Resource 实例

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

public class Demo1 {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext();
//        通过 ApplicationContext访问资源,ApplicationContext实例获取Resource实例时,默认采用与 ApplicationContext 相同的资源访问策略
        Resource res = ctx.getResource("resource.txt");
        System.out.println(res.getFilename());
    }
}

        2、使用 FileSystemApplicationContext 获取 Resource 实例

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;

public class Demo2 {

    public static void main(String[] args) {
        ApplicationContext ctx = new FileSystemXmlApplicationContext();
        Resource res = ctx.getResource("resource.txt");
        System.out.println(res.getFilename());
    }
}

5.3 ResourceLoader 总结

        Spring 将采用和 ApplicationContext 相同的策略来访问资源。也就是说,如果 ApplicationContext FileSystemXmlApplicationContextres 就是 FileSystemResource 实例;如果 ApplicationContext ClassPathXmlApplicationContextres 就是 ClassPathResource 实例。

        当 Spring 应用需要进行资源访问时,实际上并不需要直接使用 Resource 实现类,而是调用 ResourceLoader 实例的 getResource() 方法来获得资源,ReosurceLoader 将会负责选择 Reosurce 实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来。

        另外,使用 ApplicationContext 访问资源时,可通过不同前缀指定强制使用指定的 ClassPathResourceFileSystemResource 等实现类。

Resource res = ctx.getResource("calsspath:bean.xml");
Resrouce res = ctx.getResource("file:bean.xml");
Resource res = ctx.getResource("http://localhost:8080/beans.xml");

六、ResourceLoaderAware 接口

6.1 概述

        ResourceLoaderAware 接口实现类的实例将获得一个 ResourceLoader 的引用, ResourceLoaderAware 接口也提供了一个 setResourceLoader() 方法,该方法将由 Spring 容器负责调用,Spring 容器会将一个 ResourceLoader 对象作为该方法的参数传入。

        如果把实现 ResourceLoaderAware 接口的 Bean 类部署在 Spring 容器中,Spring 容器会将自身当成 ResourceLoader 作为 setResourceLoader() 方法的参数传入。由于 ApplicationContext 的实现类都实现了 ResourceLoader 接口,Spring 容器自身完全可作为 ResorceLoader 使用。

6.2 使用示例

        1、创建类,实现 ResourceLoaderAware 接口

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class TestBean implements ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    // 实现 ResourceLoaderAware 接口必须实现的方法
	// 如果把该 Bean 部署在 Spring 容器中,该方法将会有 Spring 容器负责调用。
	// SPring容 器调用该方法时,Spring 会将自身作为参数传给该方法。
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    // 返回ResourceLoader对象的应用
    public ResourceLoader getResourceLoader(){
        return this.resourceLoader;
    }

}

        2、创建 bean.xml 文件,配置 TestBean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="testBean" class="com.spring6.aware.TestBean"></bean>
</beans>

        3、测试

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

public class Demo3 {

    public static void main(String[] args) {
        // Spring 容器会将一个 ResourceLoader 对象作为该方法的参数传入
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        TestBean testBean = ctx.getBean("testBean",TestBean.class);
        // 获取ResourceLoader对象
        ResourceLoader resourceLoader = testBean.getResourceLoader();
        System.out.println("Spring容器将自身注入到 ResourceLoaderAware Bean 中 ? :" + (resourceLoader == ctx));
        // 加载其他资源
        Resource resource = resourceLoader.getResource("resource.txt");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
    }
}

七、使用 Resource 作为属性

7.1 概述

        前面介绍了 Spring 提供的资源访问策略,但这些依赖访问策略要么需要使用 Resource 实现类,要么需要使用 ApplicationContext 来获取资源。实际上,当应用程序中的 Bean 实例需要访问资源时,Spring 有更好的解决方法:直接利用依赖注入。从这个意义上来看,Spring 框架不仅充分利用了策略模式来简化资源访问,而且还将策略模式和 IoC 进行充分地结合,最大程度地简化了 Spring 资源访问。

        归纳起来,如果 Bean 实例需要访问资源,有如下两种解决方案:代码中获取 Resource 实例和使用依赖注入。

        对于第一种方式,当程序获取 Resource 实例时,总需要提供 Resource 所在的位置,不管通过 FileSystemResource 创建实例,还是通过 ClassPathResource 创建实例,或者通过 ApplicationContext getResource() 方法获取实例,都需要提供资源位置。这意味着:资源所在的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序。

        因此,通常建议采用第二种方法,让 Spring Bean 实例依赖注入资源。

7.2 使用示例

        1、创建依赖注入类,定义属性和方法

import org.springframework.core.io.Resource;

public class ResourceBean {
    
    private Resource res;
    
    public void setRes(Resource res) {
        this.res = res;
    }
    public Resource getRes() {
        return res;
    }
    
    public void parse(){
        System.out.println(res.getFilename());
        System.out.println(res.getDescription());
    }
}

        2、创建 spring 配置文件,配置依赖注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="resourceBean" class="com.spring6.resourcebean.ResourceBean" >
        <!-- 可以使用file:、http:、ftp:等前缀强制 Spring 采用对应的资源访问策略 -->
        <!-- 如果不采用任何前缀,则 Spring 将采用与该 ApplicationContext 相同的资源访问策略来访问资源 -->
        <property name="res" value="classpath:resource.txt"/>
    </bean>
</beans>

        3、测试

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo4 {

    public static void main(String[] args) {
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("bean-resource.xml");
        ResourceBean resourceBean = ctx.getBean("resourceBean",ResourceBean.class);
        resourceBean.parse();
    }
}

八、应用程序上下文和资源路径

8.1 概述

        不管以怎样的方式创建 ApplicationContext 实例,都需要为 ApplicationContext 指定配置文件,Spring 允许使用一份或多分 XML 配置文件。当程序创建 ApplicationContext 实例时,通常也是以 Resource 的方式来访问配置文件的,所以 ApplicationContext 完全支持ClassPathResourceFileSystemResourceServletContextResource 等资源访问方式。

        ApplicationContext 确定资源访问策略通常有两种方法:使用 ApplicationContext 实现类指定访问策略和使用前缀指定访问策略。

8.2 ApplicationContext 实现类指定访问策略

        创建 ApplicationContext 对象时,通常可以使用如下实现类:

//  对应使用 ClassPathResource 进行资源访问。
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-resource.xml");

// 对应使用 FileSystemResource 进行资源访问。
ApplicationContext ctx2 = new FileSystemXmlApplicationContext("bean-resource.xml");

        当使用 ApplicationContext 的不同实现类时,就意味着 Spring 使用响应的资源访问策略。

8.3 使用前缀指定访问策略

8.3.1 classpath 前缀使用

public class Demo1 {

    public static void main(String[] args) {
        /*
         * 通过搜索文件系统路径下的 xml 文件创建 ApplicationContext,
         * 但通过指定 classpath:前缀强制搜索类加载路径
         * classpath:bean.xml
         */
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("classpath:bean.xml");
        System.out.println(ctx);
        Resource resource = ctx.getResource("resource.txt");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
    }
}

8.3.2 classpath 通配符使用

        classpath * : 前缀提供了加载多个 XML 配置文件的能力,当使用 classpath*: 前缀来指定 XML 配置文件时,系统将搜索类加载路径,找到所有与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个 ApplicationContext

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean.xml");
System.out.println(ctx);

        当使用 classpath * : 前缀时,Spring 将会搜索类加载路径下所有满足该规则的配置文件。

        如果不是采用 classpath * : 前缀,而是改为使用 classpath: 前缀,Spring 则只加载第一个符合条件的 XML 文件。

        注意:classpath * :  前缀仅对 ApplicationContext 有效。实际情况是,创建 ApplicationContext 时,分别访问多个配置文件(通过 ClassLoader getResource 方法实现)。因此,classpath * : 前缀不可用于 Resource

8.3.3 通配符其他使用

        一次性加载多个配置文件的方式:指定配置文件时使用通配符。

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:bean*.xml");

        Spring 允许将 classpath*:  前缀和通配符结合使用:

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean*.xml");
### Spring 框架中的资源操作 Spring 框架提供了强大的资源加载机制,允许开发者轻松访问各种类型的资源文件。这些功能主要依赖于 `Resource` 接口及其实现类[^1]。 #### 什么是 Resource? `Resource` 是 Spring 中的一个接口,用于表示应用程序中可能使用的任何类型资源(如文件、URL 或类路径下的资源)。它封装了底层的输入流处理逻辑,使得资源操作更加统一和方便。 以下是常见的几种 `Resource` 实现类: - **ClassPathResource**: 加载类路径下的资源。 - **FileSystemResource**: 访问本地文件系统的资源。 - **UrlResource**: 处理 URL 形式的资源。 - **InputStreamResource**: 封装了一个已有的 InputStream 对象作为资源。 #### 如何使用 Resource? 下面是一个简单的示例,展示如何通过配置类来读取并操作资源: ```java import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class ResourceExample { public static void main(String[] args) throws Exception { // 创建 ClassPathResource 对象,指向类路径下 resources 文件夹内的 test.txt 文件 Resource resource = new ClassPathResource("test.txt"); // 获取资源的内容 try (var reader = new java.util.Scanner(resource.getInputStream())) { while (reader.hasNextLine()) { System.out.println(reader.nextLine()); } } // 输出资源是否存在 System.out.println("Resource exists: " + resource.exists()); } } ``` 在这个例子中,程序尝试从类路径下加载名为 `test.txt` 的文件,并将其内容逐行打印出来。 如果需要动态指定资源配置,则可以通过 Spring 配置类完成此任务。例如,在 Web 应用中定义安全策略时,可以利用自定义配置类加载外部化的认证数据或权限规则[^2]。 对于更复杂的场景,比如基于内存映射或者网络请求获取远程服务返回的数据流等情况,也可以借助相应的工具类扩展支持范围[^4]。 #### 注意事项 当涉及敏感信息存储(如同引用所提到的例子那样设置用户名密码等参数)时,请务必遵循最佳实践原则保护好相关信息的安全性[^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值