Loading resources in Spring Boot
last modified July 29, 2023
In this article we are going to show how to load resources in a Spring Boot application.
Spring is a popular Java application framework for creating enterprise applications. Spring Boot is a way to create stand-alone, production-grade Spring based applications with minimal effort.
Spring Boot Resource
Resource is data, such as images, audio, and text, that a program needs to access in a way that is independent of the location of the program code.
Because java.net.URL
is not adequate for handling all kinds
of low level resources, Spring introduced org.springframework.core.io.Resource
.
To access resources, we can use @Value
annotation or ResourceLoader
class.
Spring Boot load resource example
Our application is a Spring Boot command line application that counts the
occurrence of words in a text file. The file is located in the
src/main/resources
directory.
build.gradle ... src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ │ Application.java │ │ │ MyRunner.java │ │ └───service │ │ CountWords.java │ └───resources │ application.yml │ thermopylae.txt └───test └───java
This is the project structure.
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' }
This is the Gradle build file. Spring Boot starters are a set of convenient
dependency descriptors we can include in our application. The
spring-boot-starter
dependency is a core starter that includes
auto-configuration support, logging and YAML.
spring: main: banner-mode: "off" logging: level: org: springframework: ERROR com: zetcode: INFO
The application.yml
file contains various configuration settings of
a Spring Boot application. We have the banner-mode
property where
we turn off the Spring banner. Also, we set the logging level for spring
framework to ERROR and our application to INFO. The file is located in the in the
src/main/resources
directory.
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.
This is the text file that we read in our application. It is also located
in the src/main/resources
directory.
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
is a Spring managed bean that performs the counting
of words in the given file. The text is read from a file into a list of
sentences. The sentences are split the into words and counted.
Map<String, Integer> wordCount = new HashMap<>();
The wordCount
is a map, where keys are words and the frequency
is an integer.
List<String> lines = Files.readAllLines(Paths.get(res.getURI()), StandardCharsets.UTF_8);
We read all content in one shot with the Files.readAllLines
method.
The Files.readAllLines
method returns a list of strings.
for (String line : lines) { String[] words = line.split(" "); ...
We go through the lines and split them into words; the words are separated by spaces.
if (word.endsWith(".") || word.endsWith(",")) { word = word.substring(0, word.length()-1); }
We remove trailing dots and commas.
if (wordCount.containsKey(word)) { wordCount.put(word, wordCount.get(word) + 1); } else { wordCount.put(word, 1); }
If the word is already in the map, we increase its frequency; otherwise we insert it into the map and set its frequency to one.
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)); } } }
With the CommandLineRunner
, the Spring Boot application is run on
the terminal.
@Value("classpath:thermopylae.txt") private Resource res;
Using the @Value
annotation, we set the file into
the resource.
private final CountWords countWords; @Autowired public MyRunner(CountWords countWords) { this.countWords = countWords; }
We inject the CountWords
bean.
Map<String, Integer> words = countWords.getWordsCount(res); for (String key : words.keySet()) { System.out.println(key + ": " + words.get(key)); }
We call the getWordsCount
method and receive a map of
words and their frequencies. We iterate over the map and print the
key/value pairs to the console.
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); } }
The Application
sets up the Spring Boot application.
The @SpringBootApplication
enables auto-configuration and
component scanning.
Using ResourceLoader
Previously, we have used @Value
annotation to load the resource.
The following is an alternative solution with 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)); } } }
Alternatively, we can use ResourceLoader
to load the resource.
private final ResourceLoader resourceLoader; private final CountWords countWords; @Autowired public MyRunner(ResourceLoader resourceLoader, CountWords countWords) { this.resourceLoader = resourceLoader; this.countWords = countWords; }
The ResourceLoader
and CountWords
are injected.
Resource res = resourceLoader.getResource("classpath:thermopylae.txt");
The Resource
is obtained from the resource loader with the
getResource
method.
Running the application
The application is run on the command line.
$ ./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 ...
We run the application. The -q
option supresses the Gradle logs.
In this article we worked with resources in a Spring Boot application. We used
@Value
and ResourceLoader
to load a resource file.