Loading resources in Spring Boot

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.

Resource is data, such as images, audio, 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.

Application

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.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           ├── count
    │   │           │   └── CountWords.java
    │   │           └── MyRunner.java
    │   └── resources
    │       ├── application.yml
    │       └── thermopylae.txt
    └── test
        └── java      

This is the project structure.

pom.xml
<?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>SpringBootResourceLoader</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.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.

application.yml
spring: 
    main:
        banner-mode: "off"       

logging: 
    level: 
        org: 
            springframework: ERROR
        com:
            zetcode: ERROR

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 and our application to ERROR. The file is located in the in the src/main/resources directory.

thermopylae.txt
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.

CountWords.java
package com.zetcode.count;

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.

MyRunner.java
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.

Application.java
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. This is an alternative solution with ResourceLoader.

MyRunner.java
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. You might also be interested in the related tutorials: Spring Boot H2 REST tutorial, Spring Boot Thymeleaf tutorial, Spring Boot Mustache tutorial, Spring Boot Swing integration tutorial, Introduction to Spring web applications, Spring Boot RESTFul application, JdbcTemplate in a classic Spring application.