Spring boot is a popular choice while developing micro services. A micro service architecture may get divided into large number of small services.

Each services can have their own application configurations placed under application’s class path. Maintaining these configuration properties becomes very hard when number of services increases.

A possible solution for this can be using a common place to handle the configuration properties. We can set up a configuration server and other services can use this configuration server for required configuration properties.

First, We need to create a configuration server. This centralized configuration server, contains configuration properties related to all other micro services.

Setting up configuration server

Let’s set up our configuration server now.

Create a spring boot application with spring-cloud-config-server dependency.

Add required dependency

Following is the pom.xml file contains required dependencies for our configuration server.

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>spring-boot-config-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-config-server</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Add required configuration properties

Add the following configuration properties into application.properties file.

server.port=8888
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=classpath:/config/simple-service/
  • server.port : This is the port of configuration server.
  • spring.profiles.active : Since we are using file system back end, we have to set this property value to native.
  • spring.cloud.config.server.native.search-locations : This is the configuration file path(System file path, which is placed anywhere in current system).

Let’s place configuration files inside the directory pointed by the property spring.cloud.config.server.native.search-locations. In our example, we will use /src/main/resources/config directory of spring boot application.

Create a simple-service folder under /src/main/resources directory.

Create two configuration .yml files with name simple-service-dev.yml and simple-service-prod.yml.

These files should have the name with pattern applicationName-profile.yml. Our client spring boot application will have spring application name set to simple-service, with two active profiles dev and prod.

Both files contains a single configuration property my.prop with some random value.

simple-service-dev.yml

my:
  prop: Hello Simple Service DEVELOPMENT Environment!!!

simple-service-prod.yml

my:
  prop: Hello Simple service PRODUCTION Environment!!!!!

Enable configuration server by using @EnableConfigServer

Enable configuration server by using @EnableConfigServer annotation to spring boot application class.

@SpringBootApplication
@EnableConfigServer
public class SpringBootConfigServerApplication {

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

Test the configuration server

Our configuration server with file system back end is ready to launch!! Start the server by running the spring boot application.

configuration server started

Now we can check if configuration server is working as expected. We can access our two configuration properties by hitting respective end points.

Profile dev related configuration file is available at : http://localhost:8888/simple-service/dev.

configuration server profile dev

Profile dev related configuration file is available at : http://localhost:8888/simple-service/prod.

configuration server profile prod

Setting up configuration client

Our configuration server is ready. Let’s create a configuration client service now.

Add required dependency

Create a spring boot application with dependency spring-cloud-config-client. Complete pom.xml file is given below.

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>simple-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>simple-service</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Create bootstrap configuration file

create a file bootstrap.properties under directory src/main/resources/ and add the following properties.

bootstrap.properties

spring.profiles.active=dev,prod
spring.application.name=simple-service
spring.cloud.config.url=http://localhost:8888
  • spring.profiles.active : This property can be used to set active spring boot profile. We have added dev and prod in above example.
  • spring.application.name : This is the spring application name. Make sure that the name should match with the configuration file name(Ex : simple-service-dev.properties)
  • spring.cloud.config.url : This property points to the config server path.

Testing config clients

Modify client spring boot application class by adding a rest end point to check the configuration properties.

package com.asb.example;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class SimpleServiceApplication {

	@Value(value = "${my.prop}")
	private String myProp;
	
	@GetMapping("/")
	public String getMyProp() {
		return "Prop value: " + myProp;
	}
	
	public static void main(String[] args) {
		SpringApplication.run(SimpleServiceApplication.class, args);
	}
}

Run the config client applications with different profiles and ports using command line arguments. Let’s use port 8080 for dev profile and 8090 for prod profile.

If you are using IDE like STS, we can run application by Right click > Run as > Run Configurations. Here, specify command line arguments under Arguments tab as shown below.

command line argument sts.

Once both the servers are started, we can access them under http://localhost:8080 for dev profile and http://localhost:8090 for prod profile.

config-client-dev
config-client-prod

We have accessed configuration properties from configuration server!! Good job!! πŸ™‚ Well done!! πŸ™‚

Detect configuration changes with @RefreshScope

What if the configuration properties change when application is running?Spring cloud configuration server exposes modified properties without requiring any changes or server restart.

Client service will not detect any changes occurred to configuration properties, as it loads configuration properties during application startup.

To detect modified configuration properties, we have to annotate configuration client service with @RefrershScope annotation. Note that only custom property value changes are detected from this annotation.

Update config client service with as shown below.

@SpringBootApplication
@RestController
@RefreshScope
public class SimpleServiceApplication {
	//.....
}

Also, add spring-boot-starter-actuator dependency to expose /refresh end point for client server.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

By default, this end point is not enabled in spring boot 2 applications. To enable it add following configuration property to application.properties file.

management.endpoints.web.exposure.include=refresh

Update prod profile property to inside simple-service-prod.yml file to some different value as shown below.

Make sure both config server and client service are running before modifying configuration value.

my:
  prop: Hello Simple service PRODUCTION Environment Updated!!!

Now access the refresh end point using postman. Refresh end point is exposed as POST end point, so we can not access it from browser.

Refresh actuator end point

Refresh the browser now. We should be able to get updated configuration property value. πŸ™‚

refresh scope example spring boot

Congratulations! We have refreshed configuration property without restarting the server. Good job! πŸ™‚ πŸ™‚

Conclusion

In this article, we learned how to setup spring boot cloud configuration server. We also created a simple service, used as configuration client to read the property values.

We also learned how to refresh the modified configuration property values without server restart.

Source code is available on Github. Happy coding!! πŸ™‚ πŸ™‚

You may also interested in