Creating Custom Spring Boot Starter To Solve Cross-Cutting Concerns

A guide to build custom starter to solve cross-cutting concerns by leveraging spring’s autoconfiguration and AOP.

Photo by Clément H on Unsplash

Spring Boot takes an opinionated view of the Spring platform to enable developers to build stand-alone, production-ready applications with little effort in no time. Instead of having to configure Spring and/or 3rd-party components, such as databases or message brokers, manually like in old XML days, Spring provides out-of-the-box auto configurations.

There are certain cross-cutting concerns that we don’t want to implement from scratch for each application. Instead, we want to implement those features once and include them into any application as needed, spring boot starters helps us to achieve this. Spring Boot Starters are just dependencies that are autoconfigurable. Under the hood, the starter is going to tell Spring what is its auto configuration class, and Spring is going to invoke it when loading the context. This configuration class will create all the beans you need by default.

What is Spring Boot Auto Configuration

Spring Boot Auto configuration is an approach to automatically configure the application based on the dependencies that are present on the classpath. This can make development faster and easier by eliminating the need for defining certain beans that are included in the auto-configuration classes.

On starting our application, Spring Boot checks for a specific file spring.factories in the META-INF directory of spring’s spring-boot-autoconfigure project. Here is an entry from the spring.factories file.

# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

All auto configuration classes should be listed under EnableAutoConfiguration key in the spring.factories property file. Spring Boot will try to run all the different configuration class names mapped in this property file. However whether or not these classes will actually run depends on the presence of dependent classes on the classpath.

For example when we specify the starter dependency for cassandra database ‘spring-boot-starter-data-cassandra’ in our application. The class for cassandra will be found in the application’s class path. Therefore the class CassandraAutoConfiguration will run and the related beans will be initialized.

This conditional initialization is enabled by the @ConditionalOnClass annotation on the class CassandraAutoConfiguration. We can refer this in the CassandraAutoConfiguration class file under spring project. Code snippet specifying this is shown below.

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ CqlSession.class})
@EnableConfigurationProperties(CassandraProperties.class)
public class CassandraAutoConfiguration {
// Configuration code and other methods.
}

On the process of autoconfiguration Spring Boot initializes the beans using some defaults values on bean creation. To override those defaults, we generally declare them in the application.properties file of the consuming project. These properties are automatically picked up by the Spring Boot container.

Spring Boot uses annotations to determine if an autoconfiguration class needs to be configured or not. The @Conditional annotation that was introduced in Spring 4.0 helps autoconfiguration in spring boot. The @ConditionalOnClass and @ConditionalOnMissingClass annotations help Spring Boot to determine if an auto-configuration class needs to be included or not.

Spring Boot AOP

AOP (aspect-oriented programming) provides the way to dynamically add the cross-cutting concerns. It does so by adding additional behavior to existing code without modification of the code itself. Similar to OOPs which has class as unit of modularity, in AOP the unit of modularity is aspect. Aspects enable the modularization of concerns such as transaction management, logging, security checks etc., that cut across multiple types and objects.

Spring AOP enables Aspect-Oriented Programming in spring applications. Even though the AOP framework is one of the key components in the Spring framework, we don’t need to use AOP if we don’t need them. However spring uses AOP to provide declarative solutions such as transaction management.

AOP flow digram

Custom Starter with Spring Boot

Spring Boot Starters are a set of convenient dependency descriptors with the sole purpose of providing all dependencies necessary to “get started” with a certain module along with some default configuration values. This usually means that it’s a simple pom.xml or build.gradle file that contains dependencies to one or more auto-configure modules and any other dependencies that might be needed.

To create our own custom starter, we require following components

  • A starter module which will bring all required dependencies using pom.xml or build.gradle
  • An auto-configure module with auto configuration class.

Let’s Create a starter project that logs Method Execution Time

In this example we are creating a starter module, an auto-configure module and a service module with AOP. The service module will have the core functionality. However it is ok to include all these in a single starter module.

Create a service project with AOP to log method execution time

Create a simple annotation TrackTime which can be annotated to the client application’s method whose execution time needed to be logged.

TrackTime annotation for AOP

The above created annotation will be passed as a pointcut expression for the aspect.

Aspect defining the point cut on annotation TrackTime with advice type Around

We use aspect advice type Around for logging method execution time. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.

Creating our own Autoconfiguration project

We will create an autoconfigure class which will enable method execution time logging based on the availability of the aspect class in the class path. That means we will be using the conditional annotation @ConditionalOnClass, which allow us to specify that a configuration bean will be included if a specified class is present in the class path.

We will also use @EnableConfigurationProperties annotation to give default logger property name. This logger name can be overridden by specifying value for the property logging.track.loggerName in the application.properties file in the consuming application.

spring.factories File

To make the application pick up our Spring boot starter, we need to create a specific property file called spring.factories. This file should be placed within the src/main/resources/META-INF folder. This file should contain property org.springframework.boot.autoconfigure.EnableAutoConfigurati with all the configuration classes that need to be autoconfigured.

spring.factories

This property should contain the fully qualified name of the autoconfiguration class, in our case this will be ‘ com.vishnu.boot.logging.autoconfigure.MethodExecutionTimeAutoConfiguration

Creating Our Custom Spring Boot Starter Project

While creating a custom starter with Spring Boot you should make sure to provide a proper namespace for the starter. Do not start your module names with spring-boot, even if you use a different Maven groupId. There is nothing wrong in doing so but by convention and the naming rule followed by spring use name-spring-boot-starter as a guideline. In our case, we named our starter as logging-service-spring-boot-starter.

Since we segregated auto-configure module and service module as two different projects there are no more java files, we only need to add auto-configure and service modules as dependencies in our Custom Spring Boot Starter Project. That’s it, our starter application is ready, now we can add the starter dependency to the client applications which needs this feature.

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

<dependencies>
<dependency>
<groupId>org.vishnu.aop</groupId>
<artifactId>logging-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.vishnu.boot</groupId>
<artifactId>logging-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>

How to use the custom starter?

Let’s create a sample Spring Boot application ‘spring-rest-demo’ to use our custom starter.

Add the dependency of starter project ‘logging-spring-boot-starter’ to our demo application’s pom file.

<dependency>
<groupId>org.vishnu.boot</groupId>
<artifactId>logging-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

Annotate the method with ‘@TrackTime’ whose method execution time we want to log.

TrackTime annotation will log the execution time of the Api.

Now run the application and call the respective method or API in our case before checking the logs. The application will log the total method execution time. Since we annotated ‘@TrackTime’ in our rest controller API end point it will log the time taken for executing the particular api.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store