Loading resources in Spring Boot
last modified July 6, 2020
In this tutorial, 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,
which is the standard Maven location for application resources.
pom.xml src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ │ Application.java │ │ │ MyRunner.java │ │ └───service │ │ CountWords.java │ └───resources │ application.yaml │ thermopylae.txt └───test └───java
This is the project structure.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zetcode</groupId> <artifactId>springbootresource</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
This is the Maven build file. Spring Boot starters are a set of convenient dependency
descriptors we can include in our application. They greatly simplify Maven configuration.
The spring-boot-starter-parent
provides some common configurations of a Spring Boot
application. The spring-boot-starter
dependency is a core starter that includes
auto-configuration support, logging and YAML.
The spring-boot-maven-plugin
provides Spring Boot support in Maven, allowing us
to package executable JAR or WAR archives. Its spring-boot:run
goal runs the
Spring Boot application.
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.count.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; @Autowired private 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.
@Autowired private 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.count.CountWords; import java.util.Map; 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; @Component public class MyRunner implements CommandLineRunner { @Autowired private ResourceLoader resourceLoader; @Autowired private 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.
@Autowired private ResourceLoader resourceLoader;
ResourceLoader
is injected into the field.
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.
$ mvn spring-boot:run -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 ...
With mvn spring-boot:run
command, we run the application.
The -q
option supresses the Maven logs.
In this tutorial, we worked with resources in a Spring Boot application. We used
@Value
and ResourceLoader
to load a resource file.