Building a Custom Spring Boot Starter for Microservices

Overview

Spring Boot provides the parent POM for easier creation of Spring Boot applications. However, not everyone likes inheriting from the spring-boot-starter-parent POM.

In one of the projects I have worked earlier, we have used a custom Spring Boot Starter with all the Security Configurations and generic components required for creating microservices.

By using this starter, we were able to focus only on the buisness use case of the service rather than spending time on configuring the same thing again and again for new service implementation

Like this, You may have your own corporate standard parent that you need to use, or you may just prefer to explicitly declare all your Maven configuration.

In this tutorial, we’ll demonstrate how to build a custom Spring Boot Starter with multiple authentication profiles and default application.properties.

So far, we have implemented the following things in our previous articles

  1. Spring Boot REST API
  2. Swagger 2 integration
  3. Basic Authentication and authorization
  4. JWT Authentication and authorization
  5. LDAP Authentication and authorization
  6. Disable Spring Security for noauth profile

In order to implement a new micorservice with all the above funtionalities, we don’t need to copy the code that we have developed so far here. If we do so, it will lead to redundant code and its very difficult to maintain as well.

We are gonna create a starter module and move all the Security configurations and application.properties from the product REST API to the starter module. So that, new microservices can inherit all the common configurations/functionalities and application properties from the starter module.

To accomplish this, we are gonna create:

  1. microservice-spring-boot-starter module which uses the Spring Boot Starter Parent and defines the required dependencies in the pom.xml. Apart from that, it also contains all the security configurations and default application properties.
  2. microservice-starter-parent module which has the parent POM. The packaging type of parent project should always be POM. Then only we will be able to use it as a parent in the actual applications. This parent module defines the starter module as a dependency in the dependencyManagement section in pom.xml.
  3. microservice which makes use of the starter module via the parent POM.

Naming Convention

Custom starter module should not start with Spring Boot. Use name-spring-boot-starter as a guideline. In our case, we named our starter as microservice-spring-boot-starter.

Lets Get Started

We are gonna create a Spring Boot multi module project as shown in the diagram below

Spring Boot Multi Module Project with Custom Starter

Create Starter Module

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 https://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.2.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	
	<groupId>com.javachinna</groupId>
	<artifactId>microservice-spring-boot-starter</artifactId>
	<version>1.0</version>
	<name>microservice-spring-boot-starter</name>
	<description>Microservice Spring Boot Starter</description>
	<packaging>jar</packaging>
	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ldap</groupId>
			<artifactId>spring-ldap-core</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-beans</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-tx</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-ldap</artifactId>
		</dependency>
		<dependency>
			<groupId>com.unboundid</groupId>
			<artifactId>unboundid-ldapsdk</artifactId>
		</dependency>
	</dependencies>
</project>

We have excluded spring-beans and spring-tx dependencies from the spring-ldap-core dependency to avoid version conflicts.

Note: If you don’t wanna inherit the spring-boot-starter-parent at all, then you can still use the dependency management without inheriting it by removing the parent configuration and adding the spring-boot-dependencies in the dependencyManagement section of the pom.xml as shown below

<dependencyManagement>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.2.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Project Structure

All the files shown below are pulled from the product REST API project here. It has all the Security configurations, generic components. Any other generic funtionality like validation can be added in the starter and be used across all the microservices which makes use of this starter.

Microservice Spring Boot Starter

Install Starter with Maven

Run mvn clean install command in the starter project to build the jar and install it in your local maven repository. So that, when you build the parent and child module, it will be pulled from the local repository.

Create Parent Module

pom.xml

<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.javachinna</groupId>
	<artifactId>microservice-starter-parent</artifactId>
	<version>1.0</version>
	<packaging>pom</packaging>

	<name>microservice-starter-parent</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.javachinna</groupId>
				<artifactId>microservice-spring-boot-starter</artifactId>
				<version>1.0</version>
			</dependency>
		</dependencies>
	</dependencyManagement>
</project>

Install Parent with Maven

Run mvn clean install command in the parent project to install it in your local maven repository. So that, when you build the child module, it will be pulled from the local repository.

Create Microservice

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.javachinna</groupId>
		<artifactId>microservice-starter-parent</artifactId>
		<version>1.0</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<artifactId>spring-boot-microservice</artifactId>
	<name>spring-boot-microservice</name>
	<description>Demo project for Spring Boot REST API CRUD Operations with Swagger Documentation</description>

	<properties>
		<java.version>11</java.version>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>com.javachinna</groupId>
			<artifactId>microservice-spring-boot-starter</artifactId>
		</dependency>
	</dependencies>

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

</project>

Since we are using a custom parent, we will not get the plugin mangement feature provided by spring-boot-starter-parent. That is why we have added the spring-boot-maven-plugin with version number.

We have added dependencies that are common to all the microservices in the starter module itself. Apart from that, if any service specific dependency is required, then those should be added in the microservice pom itself.

Project Structure

All the files shown below are pulled from the product REST API project here. It has only the Product specific classes leaving all the generic configurations in starter module

Spring Boot Microservice

Inheriting application.properties

By default properties will be loaded from application.properties and/or application.yml files in the following locations:

  • file:./config/
  • file:./
  • classpath:config/
  • classpath:

The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).

Hence, we have moved the application.properties to the resources folder in the starter module and overridden few default properties in our microservice by creating a application.properties in the src\main\resources\config\ folder.

application.properties

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
logging.level.root=debug

Run with a Profile

You can run the application with any of the 4 available profiles. For example, run mvn spring-boot:run -Dspring-boot.run.profiles=basicauth and hit the url http://localhost:8080/swagger-ui.html in the browser. You should be able to execute the services with basic authentication. Similarly, if you run the application with other profiles, then the respective security configuration will be enabled.

Source Code

As always, you can get the source code from the Github below

https://github.com/JavaChinna/microservice-spring-boot-starter

Conclusion

That’s all folks! In this article, you’ve learned how to build a custom Spring Boot Starter for developing microservices with multiple authentication profiles using the starter.

I hope you enjoyed this article. Thank you for reading.

Leave a Reply