在 Spring Boot 中加载资源
最后修改于 2023 年 7 月 29 日
在本文中,我们将展示如何在 Spring Boot 应用程序中加载资源。
Spring 是一个流行的 Java 应用程序框架,用于创建企业应用程序。Spring Boot 是一种以最小的努力创建独立、生产级别的 Spring 应用程序的方式。
Spring Boot 资源
资源是数据,例如图像、音频和文本,程序需要以独立于程序代码位置的方式访问这些数据。
由于 java.net.URL
无法满足处理所有类型的底层资源的需求,Spring 引入了 org.springframework.core.io.Resource
。要访问资源,我们可以使用 @Value
注解或 ResourceLoader
类。
Spring Boot 加载资源示例
我们的应用程序是一个 Spring Boot 命令行应用程序,它计算文本文件中单词的出现次数。该文件位于 src/main/resources
目录中。
build.gradle ... src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ │ Application.java │ │ │ MyRunner.java │ │ └───service │ │ CountWords.java │ └───resources │ application.yml │ thermopylae.txt └───test └───java
这是项目结构。
plugins { id 'org.springframework.boot' version '3.1.1' id 'io.spring.dependency-management' version '1.1.0' id 'java' } group = 'com.zetcode' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter' }
这是 Gradle 构建文件。 Spring Boot 启动器是一组方便的依赖描述符,我们可以将其包含在我们的应用程序中。spring-boot-starter
依赖项是一个核心启动器,其中包括自动配置支持、日志记录和 YAML。
spring: main: banner-mode: "off" logging: level: org: springframework: ERROR com: zetcode: INFO
application.yml
文件包含 Spring Boot 应用程序的各种配置设置。我们有一个 banner-mode
属性,我们可以在其中关闭 Spring 标语。此外,我们将 spring framework 的日志级别设置为 ERROR,并将我们的应用程序设置为 INFO。该文件位于 src/main/resources
目录中。
The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece. It took place simultaneously with the naval battle at Artemisium, in August or September 480 BC, at the narrow coastal pass of Thermopylae. The Persian invasion was a delayed response to the defeat of the first Persian invasion of Greece, which had been ended by the Athenian victory at the Battle of Marathon in 490 BC. Xerxes had amassed a huge army and navy, and set out to conquer all of Greece.
这是我们在应用程序中读取的文本文件。它也位于 src/main/resources
目录中。
package com.zetcode.service; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; @Component public class CountWords { public Map<String, Integer> getWordsCount(Resource res) throws IOException { Map<String, Integer> wordCount = new HashMap<>(); List<String> lines = Files.readAllLines(Paths.get(res.getURI()), StandardCharsets.UTF_8); for (String line : lines) { String[] words = line.split("\\s+"); for (String word : words) { if (word.endsWith(".") || word.endsWith(",")) { word = word.substring(0, word.length() - 1); } if (wordCount.containsKey(word)) { wordCount.put(word, wordCount.get(word) + 1); } else { wordCount.put(word, 1); } } } return wordCount; } }
CountWords
是一个 Spring 托管 bean,它执行对给定文件中单词的计数。文本从文件读取到句子列表中。句子被分成单词并计数。
Map<String, Integer> wordCount = new HashMap<>();
wordCount
是一个 map,其中键是单词,频率是整数。
List<String> lines = Files.readAllLines(Paths.get(res.getURI()), StandardCharsets.UTF_8);
我们使用 Files.readAllLines
方法一次性读取所有内容。Files.readAllLines
方法返回一个字符串列表。
for (String line : lines) { String[] words = line.split(" "); ...
我们遍历这些行并将它们拆分成单词;单词之间用空格分隔。
if (word.endsWith(".") || word.endsWith(",")) { word = word.substring(0, word.length()-1); }
我们删除尾随的点和逗号。
if (wordCount.containsKey(word)) { wordCount.put(word, wordCount.get(word) + 1); } else { wordCount.put(word, 1); }
如果该单词已存在于 map 中,我们增加其频率;否则,我们将其插入到 map 中并将其频率设置为 1。
package com.zetcode; import com.zetcode.service.CountWords; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; @Component public class MyRunner implements CommandLineRunner { @Value("classpath:thermopylae.txt") private Resource res; private final CountWords countWords; @Autowired public MyRunner(CountWords countWords) { this.countWords = countWords; } @Override public void run(String... args) throws Exception { Map<String, Integer> words = countWords.getWordsCount(res); for (String key : words.keySet()) { System.out.println(key + ": " + words.get(key)); } } }
使用 CommandLineRunner
,Spring Boot 应用程序在终端上运行。
@Value("classpath:thermopylae.txt") private Resource res;
使用 @Value
注解,我们将文件设置为资源。
private final CountWords countWords; @Autowired public MyRunner(CountWords countWords) { this.countWords = countWords; }
我们注入 CountWords
bean。
Map<String, Integer> words = countWords.getWordsCount(res); for (String key : words.keySet()) { System.out.println(key + ": " + words.get(key)); }
我们调用 getWordsCount
方法,并接收一个单词及其频率的 map。我们遍历 map,并将键/值对打印到控制台。
package com.zetcode; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Application
设置了 Spring Boot 应用程序。@SpringBootApplication
启用了自动配置和组件扫描。
使用 ResourceLoader
之前,我们使用 @Value
注解来加载资源。以下是使用 ResourceLoader
的替代解决方案。
package com.zetcode; import com.zetcode.service.CountWords; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import java.util.Map; @Component public class MyRunner implements CommandLineRunner { private final ResourceLoader resourceLoader; private final CountWords countWords; @Autowired public MyRunner(ResourceLoader resourceLoader, CountWords countWords) { this.resourceLoader = resourceLoader; this.countWords = countWords; } @Override public void run(String... args) throws Exception { Resource res = resourceLoader.getResource("classpath:thermopylae.txt"); Map<String, Integer> words = countWords.getWordsCount(res); for (String key : words.keySet()) { System.out.println(key + ": " + words.get(key)); } } }
或者,我们可以使用 ResourceLoader
来加载资源。
private final ResourceLoader resourceLoader; private final CountWords countWords; @Autowired public MyRunner(ResourceLoader resourceLoader, CountWords countWords) { this.resourceLoader = resourceLoader; this.countWords = countWords; }
ResourceLoader
和 CountWords
被注入。
Resource res = resourceLoader.getResource("classpath:thermopylae.txt");
Resource
从资源加载器中通过 getResource
方法获得。
运行应用程序
应用程序在命令行上运行。
$ ./gradlew bootRun -q ... been: 1 Athenian: 1 alliance: 1 navy: 1 fought: 1 led: 1 delayed: 1 had: 2 during: 1 three: 1 second: 1 Greece: 3 ...
我们运行应用程序。-q
选项抑制 Gradle 日志。
在本文中,我们使用了 Spring Boot 应用程序中的资源。我们使用 @Value
和 ResourceLoader
来加载资源文件。