Spring Boot: Configuring Properties

Introduction

In this article, we'll be diving into Configuring Spring Boot Properties.

Spring allows developers to configure a vast amount of properties for their projects. Spring Boot, besides allowing developers to start off with a project from scratch a lot more easily and time friendly than Spring, also makes it a lot easier to configure properties for your applications.

There are multiple ways to configure a Spring project:

  • Java-based
  • XML-based
  • Properties-based

Java and XML-based property configuration was a classic way of configuring Spring applications before Spring Boot introduced us with an application.properties file.

This addition allows us to externally configure the application and easily access properties defined in the file.

By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.

To register a property file, you can annotate a @Configuration class with the additional @PropertySource annotation:

@Configuration
@PropertySource("classpath:custom.properties")
public class ConfigClass {
// Configuration
}

Using this method, you can register any amount of additional .properties files:

@Configuration
@PropertySource("classpath:custom.properties")
@PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}

Injecting Spring Boot Properties

Application Setup

The easiest way to start with a skeleton project is to use Spring Initializr. Select your preferred version of Spring Boot, add the Web dependency and generate it as a Maven project:

If you open the project, you will notice that a file application.properties is being kept at the src/main/resources path.

This is the default file Spring relies upon to load the properties. We can write our custom or Spring-specific properties as key-value pairs here:

message.default.welcome=Welcome...
message.default.goodbye=Goodbye...

Instead of the properties file, we can also use a .yml file and define the same properties as:

message:
  default:
    welcome: Welcome...
    goodbye: Goodbye...

This works because of the SnakeYaml jar present in the classpath. YAML files are more concise and support maps, lists, etc.

It's up to you and your team which type to use. We will be using the .properties type in this tutorial.

Injecting Properties Using @Value

Let's see how we can use these properties in a simple REST API:

@RestController
public class GreetController {

    @Value("${message.default.welcome}")
    private String welcomeMessage;

    @Value("${message.default.goodbye}")
    private String goodBye;

    @RequestMapping("/welcome")
    public String welcome() {
        return welcomeMessage;
    }

    @RequestMapping("/bye")
    public String bye() {
        return goodBye;
    }
}

This is pretty straightforward. Using the @Value annotation, we can inject the values from the application.properties file into class fields in the Spring-managed bean GreetController.

Then we have a couple of REST endpoints that simply return these values:

Using @Value allows you to set a default value if the requested one, for any reason, isn't available:

@Value("${message.default.welcome:SomeDefaultValue}")
private String welcomeMessage;

If the message.default.welcome value isn't present, the value will be set as SomeDefaultValue.

If you'd like to read more about the @Value annotation, we've got an in-depth article on that!

Injecting Properties Using @ConfigurationProperties

If our properties have some common context like the same prefix, we can use the @ConfigurationProperties annotation which will map these properties to Java objects:

@Configuration
@ConfigurationProperties(prefix = "message.default")
public class MessageProperties {

    private String welcome;
    private String goodbye;

    // Getters and Setters
}
  • @Configuration will tell Spring to create a bean of this class.
  • @ConfigurationProperties will initialize the fields with corresponding property names.

We can now use this bean in other Spring-managed beans:

@Autowired
MessageProperties messageProperties;

Overriding Properties

Naturally, as our application environment expands and changes (development, QA, production, etc.), some of our properties will change as well. These can interfere with each others if we don't segregate them in some way.

We achieve this by maintaining different files or getting the values of the properties through environment variables.

Using Spring Profiles

The most common way to write "changing" properties is to store them in different files. These files are environment-specific and our application can load them based on the environment variables.

Spring Boot provides a very elegant way to handle this.

All we have to do is follow a naming convention - application-<environment>.properties for our property files:

  • application-dev.properties
  • application-qa.properties
  • application-production.properties, etc

To notify Spring which files to use, we have to set an environment variable - spring.profiles.active.

That being said, if the value of spring.profiles.active is dev, for example, Spring boot will load the application-dev.properties file and likewise.

Note: application.properties is always loaded, irrespective of the spring.profiles.active value. If there is the same key-value present both in application.properties and application-<environment>.properties, the latter will override the former.

Typically we write all the common properties of every environment in application.properties and override environment-specific properties using the profile-specific application-<environment>.properties.

Let's see this by creating a application-dev.properties:

message.default.welcome = Welcome to DEV environment...

There are few ways to set up spring.profiles.active variable.

If we are running the application through Eclipse, we can set this in VM arguments:

We can set it in the OS environment variables, like in Windows:

Let's start our application and in the logs, you can see the dev profile being loaded:

Lets's check both of our previous REST endpoints:

As we can see, message.default.welcome value came from application-dev.properties file and message.default.goodbye property came from application.properties.

We can have multiple values in spring.profiles.active like dev,qa:

Any duplicate key would be overridden by the last profile, in the above case being qa.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

We can also pass in the spring.profiles.active as a command-line argument like:

java -jar -Dspring.profiles.active=dev greeting-service-0.0.1-SNAPSHOT.jar

Creating application.properties from Build Location

We can also override the internal properties by creating an application.properties file at the same level from where the .jar is executed. Spring context will override properties using this newly created file.

This is a great way of distributing your application to others, who can override certain properties based on their environment, like database configurations for example.

There are other ways to externalize your properties too, such as OS environment variables, command-line arguments, etc. The order in which Spring considers it can be found here.

Externalizing Properties Using Cloud Configuration Server

Many of the applications built nowadays rely on the microservice architecture. Not only are these applications deployed separately but they could have multiple instances of themselves (based on load) and the total count could easily go above 100.

Managing properties in this particular architectural style via conventional methods requires too much effort. Also, to change a property, we have to re-build the application again and deploy it or at best have to restart the application. This requires downtime, which sort of defeats the whole purpose of microservices.

Another problem with the traditional approach, especially if the properties were externalized via file or environment variables is that there's no traceability. The latest ones are always taken and we don't know what the properties were before or who changed it.

Spring Cloud Config provides a centralized, externalized, secure and easy way for storing and serving configurations for applications for different environments:

In short, we have a Config Server running as a separate application that hooks to a Git repository.

When we start up a new application (Config Client), it gets all the needed properties from the Config Server. It doesn't matter if the application existed when we set the server up or not.

Creating a Config Server

As always, we start by using Spring Initializr.

Select your preferred version of Spring Boot, add the Config Server dependency and generate it as a Maven project:

By annotating our main class with @EnableConfigServer, we mark it as being a config server:

@SpringBootApplication
@EnableConfigServer
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

And now, we have to set up a few things in the application.properties file:

server.port = 8888
spring.cloud.config.server.git.uri = https://github.com/dhananjay12/spring-cloud-config
spring.cloud.config.server.git.searchPaths = app-properties

Here, we defined the port on which config server would be running. Then we specified the Git URL that it needs to hook for properties.

Note: By default, Spring searches for the property files at the root. If we have to specify a particular folder we can provide the location via searchPaths.

Here's how the Git repo looks like:

We can start the config server now. If you'd like to check the configuration of the Spring Config Server, following the convention - http://localhost:8888/<application-name>/<spring-profiles> will show us all the needed information.

In our case, it would be - http://localhost:8888/greeting-service-cloud/default:

Creating a Config Client

Let's create the same greeting service but with a couple of extra dependencies:

Here, we created greeting-service-cloud service with Web, Config Client, and Actuator dependencies.

It has the same REST mappings as before, with the addition of @RefreshScope annotation. This annotation allows the bean to be refreshed dynamically at runtime:

@RestController
@RefreshScope
public class GreetController {

    @Value("${message.default.welcome}")
    private String welcomeMessage;

    @Value("${message.default.goodbye}")
    private String goodBye;

    @RequestMapping("/welcome")
    public String welcome() {
        return welcomeMessage;
    }
    @RequestMapping("/bye")
    public String bye() {
        return goodBye;
    }
}

Apart from application.properties we now have to create bootstrap.properties, which is loaded before the application.properties.

It is typically used by the Spring Config Client to get properties from the Spring Config Server:

spring.application.name = greeting-service-cloud
spring.cloud.config.uri = http://localhost:8888

Here, we first set the application name. The Spring Config Server will search for this file name in the Git repository and serve its contents.

We also have to mention where the Config Server is running by specifying it in spring.cloud.config.uri.

Let's start this service and take a look at the logs:

Notice that it first got the properties from the Spring Config Server.

Note: If the Config Server is not reachable, the application won't start.

Now let's check our REST endpoints:

So we externalized our properties and have nice traceability of it in our Git repository. Some important points worth noting:

  • We can use spring-profiles-active here too. If this variable is set in the Config Client environment for eg. dev, it will be passed to the Config Server while requesting properties. The config server will then look for greeting-service-cloud-dev.properties in the Git repository and serve it to the client.
  • If there is a application.properties present in the Git repository, it will be served to all the clients in addition to other files.
  • If the Config Client requests properties, for example, say dev profile, the Config Server will return application.properties, application-dev.properties and greeting-service-cloud-dev.properties. The common properties will be overridden by the last one.

Refreshing Properties without Restart

By default, the configuration values from properties files are ready or fetched at the application startup and not again. If there are some changes to be made, we still have to restart the application.

To solve this we added the Actuator dependency to our application. It provides some production-ready endpoints that can give insights regarding our application which can be used for administrative purposes.

We have to enable these endpoints manually by specifying management.endpoints.web.exposure.include = * in the application properties.

Let's add this to the Git repo and restart the application. We can check many details of our application by visiting endpoints like http://localhost:8080/actuator/env, http://localhost:8080/actuator/mappings, etc.

The one we are interested in is the /actuator/refresh. We can force a bean to refresh its configuration (i.e, to pull configuration again from the config server) by annotating the bean with @RefreshScope.

Note: If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a new instance is created.

This can be triggered by sending an empty HTTP POST request to the client’s refresh endpoint - http://<host:port>/actuator/refresh.

Let's change the value of one in the Git repository to something else:

message.default.welcome = Welcome from cloud config server changed...
message.default.goodbye = Goodbye...

management.endpoints.web.exposure.include = *

Now lets trigger the refresh endpoint:

curl localhost:8080/actuator/refresh -d {} -H "Content-Type: application/json"

Check the /welcome endpoint:

So, we were able to refresh the property of a running application without restarting it.

Conclusion

In this article, we've covered how to configure properties in our Spring Boot application.

Firstly, we've discussed simple ways to inject properties to our application and then changing/overriding these properties based on different environments.

Secondly, we've covered how to get properties from Spring Config Server and how to update properties without a rebuild or restart.

As always, the code for the examples used in this article can be found on Github.

Last Updated: January 19th, 2023
Was this article helpful?

Make Clarity from Data - Quickly Learn Data Visualization with Python

Learn the landscape of Data Visualization tools in Python - work with Seaborn, Plotly, and Bokeh, and excel in Matplotlib!

From simple plot types to ridge plots, surface plots and spectrograms - understand your data and learn to draw conclusions from it.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms