从零开始 Spring Boot 28:资源

这篇具有很好参考价值的文章主要介绍了从零开始 Spring Boot 28:资源。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从零开始 Spring Boot 28:资源

从零开始 Spring Boot 28:资源

图源:简书 (jianshu.com)

Resource 接口

Spring中的资源被抽象为一个Resource接口:

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isReadable();

    boolean isOpen();

    boolean isFile();

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    ReadableByteChannel readableChannel() throws IOException;

    long contentLength() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}

Resource 接口中最重要的一些方法是。

  • getInputStream(): 定位并打开资源,返回一个用于读取资源的 InputStream。我们期望每次调用都能返回一个新的 InputStream。关闭该流是调用者的责任。
  • exists(): 返回一个 boolean 值,表示该资源是否以物理形式实际存在。
  • isOpen(): 返回一个 boolean,表示该资源是否代表一个具有开放流的句柄。如果为 trueInputStream 不能被多次读取,必须只读一次,然后关闭以避免资源泄漏。对于所有通常的资源实现,除了 InputStreamResource 之外,返回 false
  • getDescription(): 返回该资源的描述,用于处理该资源时的错误输出。这通常是全路径的文件名或资源的实际URL。

内置的Resource实现

Spring内置了一些Resource接口的实现类:

  • UrlResource
  • ClassPathResource
  • FileSystemResource
  • PathResource
  • ServletContextResource
  • InputStreamResource
  • ByteArrayResource

这里介绍几个常见的Resource实现类。

ClassPathResource

ClassPathResource是最常见的,通过它我们可以访问ClassPath中的文件。

举例说明,假如在Spring的静态资源目录resources下有一个文件override.properties

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

可以通过下面的示例代码将其内容打印到控制台:

package com.example.resource.controller;
// ...
@RestController
@RequestMapping("/hello")
public class HelloController {
    @GetMapping("")
    public String hello() throws IOException {
        Resource resource = new ClassPathResource("override.properties");
        printContent(resource);
        return Result.success().toString();
    }

    private void printContent(Resource resource) throws IOException {
        File file;
        try {
            file = resource.getFile();
        } catch (FileNotFoundException e) {
            String content = resource.getContentAsString(StandardCharsets.UTF_8);
            System.out.println(content);
            return;
        }
        printContent(file);
    }

    private void printContent(File file) throws IOException {
        FileReader fr;
        fr = new FileReader(file);
        BufferedReader br = new BufferedReader(fr);
        String line;
        do {
            line = br.readLine();
            if (line == null) {
                break;
            }
            System.out.println(line);
        }
        while (true);
        br.close();
    }
}

这其中下面这行代码,明确创建了一个到resources/override.properties文件的资源:

Resource resource = new ClassPathResource("override.properties");

如果Spring项目是通过IDE运行的,那么resources这个静态资源目录会被加入ClassPath,因此自然可以通过ClassPathResource正确访问到,如果项目是打包成Jar包运行,该目录同样会被打包的Jar包中的/BOOT-INF/classes目录:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CuYSJIK3-1684032458114)(D:\image\typora\image-20230513172429162.png)]

该目录同样会被加入ClassPath中,所以同样可以获取到正确的文件。

正是因为这样的特性,所以在开发中通常都会用ClassPath的方式引用静态资源,而非文件路径。因为后者可能导致部署的目标服务器上缺少相应的资源而出错。

FileSystemResource

FileSystemResource是通过文件系统来访问资源,具体来说就是文件的相对路径和绝对路径。

同样是上面的示例,只需要稍微修改:

    // ...
	@GetMapping("")
    public String hello() throws IOException {
        Resource resource = new FileSystemResource("src/main/resources/override.properties");
        printContent(resource);
        return Result.success().toString();
    }
	// ...

当然也可以使用绝对路径:

Resource resource = new FileSystemResource("D:/workspace/learn_spring_boot/ch28/resource/src/main/resources/override.properties");

UrlResource

通过UrlResource可以访问用URL定义的资源,当然最常见的是网络资源:

Resource resource = new UrlResource("https://blog.icexmoon.cn/");

要说明的是,通过UrlResource创建的Resource,是无法通过调用Resource.getFile()方法获取文件的,会产生FileNotFoundException异常。因此只能是以Resource.getInputStream()方法获取输入流,然后再打印内容。不过Resource接口其实已经提供了一个getContentAsString()方法:

public interface Resource extends InputStreamSource {
    // ...
	default String getContentAsString(Charset charset) throws IOException {
        return FileCopyUtils.copyToString(new InputStreamReader(this.getInputStream(), charset));
    }
    // ...
}

URL实际上也可以指定本地文件系统:

Resource resource = new UrlResource("file://D:/workspace/learn_spring_boot/ch28/resource/src/main/resources/override.properties");

其它的Resource实现可以阅读核心技术 (springdoc.cn)。

ResourceLoader 接口

可以通过接口ResourceLoader来获取Resource

public interface ResourceLoader {

    Resource getResource(String location);

    ClassLoader getClassLoader();
}

所有的application context都实现了这个接口,可以当做ResourceLoader来使用:

@RestController
@RequestMapping("/hello")
public class HelloController {
    @Autowired
    private ApplicationContext ctx;
    @GetMapping("")
    public String hello() throws IOException {
        Resource resource = ctx.getResource("classpath:override.properties");
        printContent(resource);
        return Result.success().toString();
    }
}

实际上在这个示例中,直接注入ResourceLoader更为合适:

@RestController
@RequestMapping("/hello")
public class HelloController {
    @Autowired
    private ResourceLoader resourceLoader;
    @GetMapping("")
    public String hello() throws IOException {
        Resource resource = resourceLoader.getResource("classpath:override.properties");
        printContent(resource);
        return Result.success().toString();
    }
    // ...
}

作为参数传入getResource方法的classpath:override.properties这样的被称作资源字符串

前缀 示例 说明
classpath: classpath:com/myapp/config.xml 从classpath加载。
file: file:///data/config.xml 作为 URL 从文件系统加载。另请参见FileSystemResource 注意事项.
https: https://myserver/logo.png URL 形式加载。
(none) /data/config.xml 取决于底层的 `ApplicationContext’。

如果资源字符串不带前缀(比如classpath:),ResourceLoader获取资源的行为根据ApplicationContext的类型的不同而不同,比如ClassPathXmlApplicationContext默认会以ClassPathResource的方式获取资源,FileSystemXmlApplicationContext默认会以FileSystemResource的方式获取资源。

ResourcePatternResolver 接口

ResourcePatternResolver接口是ResourceLoader的扩展:

public interface ResourcePatternResolver extends ResourceLoader {

    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String locationPattern) throws IOException;
}

ResourceLoader的基础上,getResources()方法增加了对通配符的支持:

@RestController
@RequestMapping("/hello")
public class HelloController {
    @Autowired
    private ResourcePatternResolver resourcePatternResolver;

    @GetMapping("")
    public String hello() throws IOException {
        Resource[] resources = resourcePatternResolver.getResources("classpath:*.properties");
        for(Resource r: resources){
            System.out.println(r.getFilename());
        }
        if (resources == null || resources.length == 0){
            return Result.fail("没有获取到文件").toString();
        }
        Resource resource = resources[0];
        printContent(resource);
        return Result.success().toString();
    }
	// ...
}

这个示例中,resourcePatternResolver.getResources("classpath:*.properties")可以匹配到resource目录下所有以.properties为后缀的文件作为资源对象返回。

如果需要从多个jar中检索同样的包名下的资源,可以使用classpath*:这样的前缀配合通配符检索。

ResourceLoaderAware 接口

可以让bean通过实现ResourceLoaderAware接口的方式获取ResourceLoader

@RestController
@RequestMapping("/hello")
public class HelloController implements ResourceLoaderAware {
	// ...
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        Resource resource = resourceLoader.getResource("classpath:override.properties");
        try {
            printContent(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

当然,相比直接注入ResourceLoader,这样做并没有什么优势。

注入Resource

可以借助@Value注解直接注入Resource

@RestController
@RequestMapping("/hello")
public class HelloController implements ResourceLoaderAware {
	// ...
    @Value("${my.properties}")
    @Autowired
    private Resource resource;

    @GetMapping("")
    public String hello() throws IOException {
    	printContent(resource);
        return Result.success().toString();
    }
    // ...
}

Spring Boot默认的配置文件application.properties

my.properties=classpath:override.properties

当然,通过setter或构造器注入也是可以的,这里不再演示。

资源字符串中使用了通配符,可以注入所有匹配的资源:

my.all.properties=classpath:*.properties
@RestController
@RequestMapping("/hello")
public class HelloController implements ResourceLoaderAware {
    // ...
    @Value("${my.all.properties}")
    @Autowired
    private Resource[] resources;

    @GetMapping("")
    public String hello() throws IOException {
        for (Resource r : resources) {
            System.out.println(r.getFilename());
        }
        if (resources == null || resources.length == 0) {
            return Result.fail("没有获取到文件").toString();
        }
        Resource resource = resources[0];
        printContent(resource);
        return Result.success().toString();
    }
    // ...
}

本文所有的示例代码可以通过learn_spring_boot/ch28/resource获取。

谢谢阅读。文章来源地址https://www.toymoban.com/news/detail-448238.html

参考资料

  • 核心技术 (springdoc.cn)

到了这里,关于从零开始 Spring Boot 28:资源的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 从零开始学Spring Boot系列-集成mybatis

    在Spring Boot的应用开发中,MyBatis是一个非常流行的持久层框架,它支持定制化SQL、存储过程以及高级映射。在本篇文章中,我们将学习如何在Spring Boot项目中集成MyBatis,以便通过MyBatis进行数据库操作。 首先,我们需要在项目中添加MyBatis的依赖。在Spring Boot中,我们通常会使

    2024年03月10日
    浏览(11)
  • 从零开始 Spring Boot 63:Hibernate 继承映射

    从零开始 Spring Boot 63:Hibernate 继承映射

    图源:简书 (jianshu.com) 关系型数据库设计中是不存在继承概念的,但实体类可以用继承来组织代码结构,所以需要用一种方式将实体类的继承结构映射到表结构。 本文将介绍几种在 JPA(Hibernate)中映射实体类继承层次的方式。 第一种方式是用 @MappedSuperclass 标记超类(Super

    2024年02月12日
    浏览(9)
  • Spring Boot(04):让你的Spring Boot应用“火力全开”,从零开始学习starter

    Spring Boot(04):让你的Spring Boot应用“火力全开”,从零开始学习starter

            Spring Boot是一款非常流行的Java开发框架,其具有快速开发、自动化配置、内嵌服务器、易于扩展等特点,因此备受开发者欢迎。在日常开发中,我们经常需要在不同的环境中进行测试和部署,此时,如何实现开发、测试、生产环境的快速切换,成为了我们需要解决

    2024年04月13日
    浏览(11)
  • 从零开始 Spring Boot 37:初始化 ApplicationContext

    从零开始 Spring Boot 37:初始化 ApplicationContext

    图源:简书 (jianshu.com) 从前文可以知道,作为 Ioc 容器的 ApplicationContext,需要进行一系列步骤来初始化以最终就绪(对于 Web 应用来说就是可以提供Http服务)。 这些步骤大概可以分为以下内容: 准备上下文关联的 Environment 。 初始化 ApplicationContext( ApplicationContextInitializers

    2024年02月08日
    浏览(11)
  • 从零开始 Spring Boot 38:Lombok 与依赖注入

    从零开始 Spring Boot 38:Lombok 与依赖注入

    图源:简书 (jianshu.com) 在之前的文章中,我详细介绍了 Lombok 的用法,考虑到在 Spring 中使用依赖注入(DI)是如此的频繁,因此有必要讨论使用 Lombok 时可能对依赖注入造成的影响。 我们都知道,Spring 中的依赖注入分为三种情况: 通过属性进行依赖注入。 通过构造器进行依

    2024年02月08日
    浏览(14)
  • 从零开始学Spring Boot系列-外部化配置

    从零开始学Spring Boot系列-外部化配置

    Spring Boot 允许你将配置外部化,以便可以在不同的环境中使用相同的应用程序代码。可以使用属性文件、YAML文件、环境变量和命令行参数将配置外部化。属性值可以通过使用 @Value 注解直接注入 bean,可以通过 Spring 的 Environment 抽象访问,也可以通过 @ConfigurationProperties。 Sp

    2024年04月10日
    浏览(12)
  • 从零开始 Spring Boot 52:@Embedded 和 @Embeddable

    从零开始 Spring Boot 52:@Embedded 和 @Embeddable

    图源:简书 (jianshu.com) 这篇文章会介绍 @Embedded 和 @Embeddable 两个注解在 JPA 中的用法。 先看一个示例: 这里使用了 Lombok 相关注解(比如 @Builder )帮助构建实体类,详细内容可以阅读我的相关文章。 user_student 是一个学生表,其中的 contacts_ 开头的字段保存联系人信息,这体

    2024年02月12日
    浏览(7)
  • 从零开始 Spring Boot 49:Hibernate Entity Lifecycle

    从零开始 Spring Boot 49:Hibernate Entity Lifecycle

    图源:简书 (jianshu.com) 本文将介绍 Hibernate 的 Session 接口,以及如何用 Session 的相关 API 转换实体(Entity)的生命周期状态。 如果缺少的 JPA 和 Hibernate 的基本认识,可以阅读前篇文章。 持久化上下文 在 JPA 的相关概念中,存在一个 持久化上下文 (Persistence Context)。 持久化

    2024年02月11日
    浏览(32)
  • 从零开始 Spring Boot 57:JPA中的一对多关系

    从零开始 Spring Boot 57:JPA中的一对多关系

    图源:简书 (jianshu.com) 在上篇文章中我们介绍了如何在 JPA 中实现实体的一对一关系,在关系型数据库设计中,除了一对一关系,还存在一对多关系。本篇文章介绍如何在 JPA 中实现一对多关系。 假设我们有两张表,学生表和电子邮件账号表,一个学生可以有多个电子邮件账

    2024年02月12日
    浏览(13)
  • 从零开始 Spring Boot 51:JPA 中的默认列值

    从零开始 Spring Boot 51:JPA 中的默认列值

    图源:简书 (jianshu.com) JPA 是一个 ORM 框架,因此,通常我们需要在实体类中定义表结构,这其中就包含可能的字段默认值。 本文介绍如何在 Hibernate(JPA)中设置默认列值(Default Column Value)。 最简单的方式是对实体类指定一个默认的属性值,比如: 测试用例: 这样做的缺点

    2024年02月11日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包