How to build a custom Spring Boot Starter in no time

In this tutorial we will learn how to create a custom Spring Boot Starter. Spring Boot starters make development easier and rapid. There are several built-in starters for most common tasks, however you can create your own starter too in order to reuse some functionalities across your applications.

Our basic Spring Boot starter will print a text string using ascii art. We will name it spring-boot-starter-

In the Spring Boot Framework, all the starters follow a similar naming pattern: spring-boot-starter-*, where * denotes a particular type of application. For example, if we want to use Spring and JPA for database access, we need to include the spring-boot-starter-data-jpa dependency in our pom.xml file of the project.

Let’s start from the pom.xml file:

<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.masterspringboot</groupId> 	<artifactId>spring-boot-starter-banner-service</artifactId> 	<version>1.0.0</version> 	<packaging>jar</packaging>  	<name>my-org-starter-security</name> 	<url>http://maven.apache.org</url>  	<properties> 		<java.version>1.8</java.version> 		<maven.compiler.source>1.8</maven.compiler.source> 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 		<maven.compiler.target>1.8</maven.compiler.target> 		<spring.boot.version>2.1.9.RELEASE</spring.boot.version> 		<spring.framework.version>5.2.1.RELEASE</spring.framework.version> 	</properties>  	<dependencies> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter</artifactId> 			<version>${spring.boot.version}</version> 			<scope>compile</scope> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-autoconfigure</artifactId> 			<version>${spring.boot.version}</version> 		</dependency>  		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-configuration-processor</artifactId> 			<version>${spring.boot.version}</version> 			<optional>true</optional> 		</dependency>  	</dependencies>  	<build> 		<plugins> 			<plugin> 				<groupId>org.apache.maven.plugins</groupId> 				<artifactId>maven-jar-plugin</artifactId> 				<version>3.1.2</version> 				<configuration> 					<archive> 						<addMavenDescriptor>false</addMavenDescriptor> 						<manifest> 							<addDefaultImplementationEntries>false</addDefaultImplementationEntries> 							<addDefaultSpecificationEntries>false</addDefaultSpecificationEntries> 						</manifest> 					</archive> 				</configuration> 			</plugin> 		</plugins> 	</build> </project>  

Since this starter will use autoconfiguration features, we have added its dependency, along to the main spring-boot-starter.

Here is the AutoConfiguration class:

package com.masterspringboot.config;

import com.masterspringboot.service.MyBannerService;
import com.masterspringboot.service.MyBannerServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration @ConditionalOnClass(MyBannerService.class) @EnableConfigurationProperties(MyBannerProperties.class)

public class MyBannerAutoConfiguration {
  @Autowired private MyBannerProperties greeterProperties;

  //conditional bean creation     

  @Bean @ConditionalOnMissingBean public MyBannerService helloService() {
    return new MyBannerServiceImpl(greeterProperties);
  }
}

This class is responsible to create the “MyBannerService” with the properties injected from MyBannerProperties.

The @ConditionalOnMissingBean annotation is used to load a bean only if a given bean is missing.

The above bean will get loaded by Spring only if there is no other bean of this type present in the context. On the other hand, if there is already a bean of the type “MyBannerService” present in the application context, the above bean will not be created.

Then, within the class MyBannerProperties we have coded just the following properties to customize the size and font of the banner:

package com.masterspringboot.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mybanner")
public class MyBannerProperties {
  String fontName;
  int fontSize;

  public String getFontName() {
    return fontName;
  }

  public void setFontName(String fontName) {
    this.fontName = fontName;
  }

  public int getFontSize() {
    return fontSize;
  }

  public void setFontSize(int fontSize) {
    this.fontSize = fontSize;
  }
}

The actual implemention is contained in the MyBannerServiceImpl which uses java.awt under the hoods to create a BufferedImage and then render a text in it:

package com.masterspringboot.service;

import com.masterspringboot.config.MyBannerProperties;
import java.awt.*;
import java.awt.image.BufferedImage;

public class MyBannerServiceImpl implements MyBannerService {
  MyBannerProperties properties;

  public MyBannerServiceImpl(MyBannerProperties greeterProperties) {
    this.properties = greeterProperties;
  }

  @Override
  public void hello(String text) {
    int width = 100;
    int height = 30;
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics g = image.getGraphics();
    g.setFont(new Font(properties.getFontName(), Font.BOLD, properties.getFontSize()));
    Graphics2D graphics = (Graphics2D) g;
    graphics.setRenderingHint(
        RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    graphics.drawString(text, 10, 20);
    for (int y = 0; y < height; y++) {
      StringBuilder sb = new StringBuilder();
      for (int x = 0; x < width; x++) {
        sb.append(image.getRGB(x, y) == -16777216 ? " " : "$");
      }
      if (sb.toString().trim().isEmpty()) {
        continue;
      }
      System.out.println(sb);
    }
  }
}

Finally, the interface for this service is:

package com.masterspringboot.service;

public interface MyBannerService {
  void hello(String text);
}

We are done with the Starter code. How does Spring Boot load this auto-configuration class? In order to use the power of the auto-configuration, you need to create it in the META-INF/spring.factories file. You specify the class that holds the auto-configuration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.masterspringboot.config.MyBannerAutoConfiguration 

That’s all. Build the starter:

$ mvn install 

Using the custom starter in your Spring Boot application

To use the custom Spring Boot Starter in your application, it is sufficient to resolve its dependency:

<?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.1.9.RELEASE</version>
       		
      <relativePath/>
       
      <!-- lookup parent from repository -->
       	
   </parent>
    	
   <artifactId>demo-app</artifactId>
    	
   <name>my-demo-app</name>
    	
   <description>Demo project for Spring Boot</description>
     	
   <properties>
       		
      <java.version>1.8</java.version>
       	
   </properties>
     	
   <dependencies>
       		
      <dependency>
          			
         <groupId>com.masterspringboot</groupId>
          			
         <artifactId>spring-boot-starter-banner-service</artifactId>
          			
         <version>1.0.0</version>
          		
      </dependency>
       	
   </dependencies>
     	
   <build>
       		
      <plugins>
          			
         <plugin>
             				
            <groupId>org.springframework.boot</groupId>
             				
            <artifactId>spring-boot-maven-plugin</artifactId>
             			
         </plugin>
          		
      </plugins>
       	
   </build>
    
</project>

Here is the main application class:

package com.masterspringboot;

import com.masterspringboot.service.MyBannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CustomApplicationStarter implements CommandLineRunner {
  @Autowired MyBannerService service;

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

  @Override
  public void run(String... strings) throws Exception {
    service.hello("Hello");
  }
}

Our application.properties will set the size and font name as follows:

mybanner.fontSize=16 mybanner.fontName=SansSerif 

If you run it, you will see a nice ascii text banner in action:

$ mvn install spring-boot:run     2021-01-30 16:09:00.741  INFO 30241 --- [           main] c.m.CustomApplicationStarter             : Started CustomApplicationStarter in 0.758 seconds (JVM running for 4.136)            $$$$   $$$$             $$$$ $$$$                                                                    $$$$   $$$$             $$$$ $$$$                                                                    $$$$   $$$$             $$$$ $$$$                                                                    $$$$   $$$$   $$$$$$$   $$$$ $$$$  $$$$$$$                                                           $$$$   $$$$  $$$$$$$$$  $$$$ $$$$ $$$$$$$$$                                                          $$$$$$$$$$$ $$$$$$$$$$  $$$$ $$$$$$$$$$$$$$$                                                         $$$$$$$$$$$ $$$$$$$$$$$ $$$$ $$$$$$$$   $$$$                                                         $$$$   $$$$ $$$$$$$$$$$ $$$$ $$$$$$$$   $$$$                                                         $$$$   $$$$ $$$$        $$$$ $$$$$$$$   $$$$                                                         $$$$   $$$$ $$$$$$$$$$  $$$$ $$$$$$$$$$$$$$$                                                         $$$$   $$$$  $$$$$$$$$  $$$$ $$$$ $$$$$$$$$                                                          $$$$   $$$$   $$$$$$$$  $$$$ $$$$  $$$$$$$                                                 

That’s it. We just covered how to develop a basic example of a custom Spring Boot Starter.

You can find the custom Spring Boot starter and the demo application here: https://github.com/fmarchioni/masterspringboot/tree/master/starters