Custom Spring Boot 2 Starter with Spring 5, chapter 1

TSBS Project repository and archives

As a shortcut, TSBS stands for Tooling Spring Boot Starter until I find a better name.

You can find TSBS sources at GitHub project page and this chapter archive under its own tag Chapter 1 archive

Available chapters so far…​

  • chapter 1 : Custom Spring Boot 2 Starter with Spring 5, chapter 1

  • chapter 2 : Spring Boot Starter : Chapter 2, error handling

  • chapter 3 : Spring Boot Starter : Chapter 3, logger and process unique id

Introduction

Spring Boot features many starter dependencies with various purposes, such as spring-boot-starter-web for web and rest features, spring-boot-starter-jpa for database orm, etc…​

A starter may be seen as a library, with an auto-configuration feature. It could be used out-of-the-box without any further configuration, unless you choose to setup something outside defined starter standards.

A starter is a good way to avoid rewriting, while keeping common code well organized and compatible with Spring way of doing things.

Our goal here is to build a custom starter with error management and monitoring features. We’ll proceed step by step to get a complete project, including a couple of starters, commons library, and also an App which provides unit testing and also demonstrates our starter usage.

I’m using Maven to build this project. I’ve tried to switch to Gradle, but for a few reasons I’ve decided to stick with Maven, although I know its caveats. I’ve also read a few articles about Maven vs Gradle ; in some way, I concluded that Gradle may be as complex as Maven. I’m way more experienced with Maven.

But feel free to contribute / fork to help me :)

Objectives

In this first post, we will :

  • Bootstrap a multi-module maven project

  • Create our Spring Boot Starter module

  • Create a demo app to test all of previous steps

Project structure

In this section, we will define our Maven configuration.

We’re using a multi-module Maven project ; this allows to create several modules attached to a parent project. This could be quite tricky to bootstrap, as Maven sometimes be complex to understand.

I’m using Spring BOMs instead of using String parent project. All modules refer to tooling-parent, and each module use Spring Boot BOM or Spring framework BOM.

project stucture
tooling-parent                   parent module
 +-tooling-commons               java utils and common classes
 +-tooling-spring-boot-starter   our Spring Boot Starter module
 +-tooling-app                   testable demo app
 +-tooling-domain                database commons - for future use.

Pom source files are stripped to be concise, you can find complete versions on github

Parent project

parent project xml
<project ...>

        <groupId>net.kprod.tooling.spring</groupId>
        <artifactId>tooling-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>pom</packaging>

        <name>tooling-parent</name>
        <description>Spring Tooling parent project</description>

  <!-- Define some project wise properties -->
        <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
                <java.version>1.8</java.version>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
    <!-- Set Spring Boot and Spring framework versions accordingly -->
    <spring.boot.version>2.1.4.RELEASE</spring.boot.version>
                <spring.platform.version>5.1.6.RELEASE</spring.platform.version>
        </properties>

        <modules>
                <module>tooling-app</module>
                <module>tooling-spring-boot-starter</module>
                <module>tooling-commons</module>
                <module>tooling-domain</module>
        </modules>
</project>

Spring Boot Starter module maven configuration

tooling-spring-boot-starter pom.xml
<project ...>

<parent>
  <groupId>net.kprod.tooling.spring</groupId>
  <artifactId>tooling-parent</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</parent>

<groupId>net.kprod.tooling.spring</groupId>
<artifactId>tooling-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>tooling-spring-boot-starter</name>
<description>Spring Boot Starter module</description>

<!-- Spring Boot platform BOM -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <!-- inherited spring boot version -->
      <version>${spring.boot.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <!-- this dependency is needed to create a starter -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
  </dependency>
  <!-- We provide our tooling-commons dependency -->
  <dependency>
    <groupId>net.kprod.tooling.spring</groupId>
    <artifactId>tooling-commons</artifactId>
    <version>${project.version}</version>
  </dependency>
  <!-- others spring dependencies... -->
</dependencies>

<!-- ... -->

</project>

Common module maven configuration

tooling-spring-boot-starter pom.xml
<project ...>
        <parent>
                <groupId>net.kprod.tooling.spring</groupId>
                <artifactId>tooling-parent</artifactId>
                <version>0.0.1-SNAPSHOT</version>
        </parent>

        <groupId>net.kprod.tooling.spring</groupId>
        <artifactId>tooling-commons</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>

        <name>tooling-commons</name>
        <description>Tooling Spring Commons components</description>

        <!-- Spring platform BOM -->
        <dependencyManagement>
                <dependencies>
                        <dependency>
                                <groupId>org.springframework</groupId>
                                <artifactId>spring-framework-bom</artifactId>
        <!-- inherited version from parent. spring version matches spring boot version -->
                                <version>${spring.platform.version}</version>
                                <type>pom</type>
                                <scope>import</scope>
                        </dependency>
                </dependencies>
        </dependencyManagement>

        <dependencies>
    <!-- We will use some spring-web dependencies such as HttpStatus -->
                <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-web</artifactId>
                </dependency>
        </dependencies>

</project>

App module maven configuration

This module provides quite the same configuration than tooling-spring-boot-starter module, unless it is not a starter.

We provide our starter dependency as follows :

tooling-spring-boot-starter dependency
<dependency>
  <groupId>net.kprod.tooling.spring</groupId>
  <artifactId>tooling-spring-boot-starter</artifactId>
  <version>${project.version}</version>
</dependency>

Spring basics

Starter module

First, we need to provide the auto-configuration class, as follows :

starter auto-configuration class
package net.kprod.tooling.spring.starter;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("net.kprod.tooling.spring.starter")
public class ToolingAutoConfiguration {
}

Then, a starter requires a spring.factories file located under resources/META-INF. This file might links to our auto-configuration class.

spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=net.kprod.tooling.spring.starter.ToolingAutoConfiguration

SpringApplication classes

Our tooling-app module will feature a complete Spring Boot Application which implements our tooling-spring-boot-starter module.

We will just create a simple main class as follows :

Spring Boot Application class
package net.kprod.tooling.spring.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class ToolingApp {

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

We also need a SpringApplication inside tooling-spring-boot-starter module in order to let spring-boot-maven-plugin to work properly. But it won’t be used as is.

The class would be quite the same, tooling-spring-boot-starter Spring Application

Testing

To complete this first chapter, we will use maven to build the project, and run our App.

maven build
mvn compile

[INFO] Scanning for projects...
[LOT OF LOGS]
[INFO] Reactor Summary:
[INFO]
[INFO] tooling-parent ..................................... SUCCESS [  0.004 s]
[INFO] tooling-commons .................................... SUCCESS [  1.889 s]
[INFO] tooling-spring-boot-starter ........................ SUCCESS [  1.126 s]
[INFO] tooling-app ........................................ SUCCESS [  0.255 s]
[INFO] tooling-domain ..................................... SUCCESS [  0.033 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.941 s
[INFO] Finished at: 2019-04-22T10:49:20+02:00
[INFO] Final Memory: 27M/172M
[INFO] ------------------------------------------------------------------------
run tooling-app
#from parent root directory
mvn install
cd tooling-app
#run spring boot app
mvn spring-boot:run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
:: Spring Boot ::        (v2.1.4.RELEASE)

2019-04-22 10:52:17.491  INFO 12089 --- [           main] net.kprod.tooling.spring.app.ToolingApp  : Starting ToolingApp on shrike with PID 12089 (/home/kemkem/Work/repo/tooling-spring-parent/tooling-app/target/classes started by kemkem in /home/kemkem/Work/repo/tooling-spring-parent)
2019-04-22 10:52:17.497  INFO 12089 --- [           main] net.kprod.tooling.spring.app.ToolingApp  : No active profile set, falling back to default profiles: default
2019-04-22 10:52:20.293  INFO 12089 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
#[...some java logs...]
2019-04-22 10:52:22.161  INFO 12089 --- [on(1)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 10 ms

To be continued…​

We now have a nice base to start coding with.

In chapter 2 we will start to write some classes to handle exception and errors. See you there !

All available chapters

  • chapter 1 : Custom Spring Boot 2 Starter with Spring 5, chapter 1

  • chapter 2 : Spring Boot Starter : Chapter 2, error handling

  • chapter 3 : Spring Boot Starter : Chapter 3, logger and process unique id

comments powered by Disqus