Building a Custom Spring Boot Starter

Spring Boot has become incredibly popular since it was first released in 2013. Starting from scratch; a developer can have an application communicating with a database through RESTful endpoints that have security configured for them all within the span of an hour. This was a huge improvement over the often days it would take to accomplish similar tasks using just the core Spring Framework.

Spring Boot itself has some core functionality, but much of what Spring Boot application will be doing; communicating with a database, providing REST endpoints, and processing security on incoming requests, are supplied through starters. The Spring team, and third party vendors that integrate with Spring, have created many publicly available starters which can be seen on start.spring.io.

These starters are great, but organizations have domain specific concerns. Organizations typically only use certain database vendors, have specific requirements when implementing security, among many other needs. Here it can be beneficial to create custom Spring Boot Starters that incorporate these constraints and requirements. In this article we will walk through the process of creating a custom Spring Boot starter.

Not Just Another Shared Library

Organizations creating shared libraries to handle cross-cutting concern like data access or security isn’t new. What is new is the Spring team has provided several extension points within Spring Boot that can be used to improve the developer experience, these shared libraries are called “starters”. Improving developer experience can increase the active use of a library which can help in making sure standards are followed across an organization.

Starter Components

There are several extensions points the Spring team has provided for building starters they are:

  • Auto-Configuration
  • Conditionals
  • Metadata for Configuration

We will step through how to use each of these features when building a starter so that it; requires minimal configuration to be used, is flexible for a variety of scenarios, and provides developers with information they need to configure it.

We will use security, my-org-starter-security, as an example for building the custom starter. Security is a common concern for organizations, it is also something that require some amount of configuration, and also might need to behave differently depending on context. This will give us a good opportunity to flex out all the above features in a semi-realistic scenario.

You can find the code example used in this article on my GitHub.

Auto-Configuration

Spring Boot is often described as being “opinionated”, one of the ways this is done is through auto-configuration. Spring components can be automatically (automagically?) loaded into context without requiring developer intervention. This is great for making your starter easier to consume and also used correctly (i.e. ensure required classes/beans are loaded). To have a component be auto-loaded requires following a few steps:

1. Add spring-boot-autoconfigure as a dependency

For auto-configuration to work, and to have the appropriate classes available, the spring-boot-autoconfigure dependency must be on classpath. The dependency can be added directly, or indirectly by including another starter as a dependency. In my example I am using spring-boot-starter as a dependency.

2. Create a Configuration class

Create a normal configuration class like you would within a Spring Boot application and have it perform whatever required behavior you need. In this example we will have the @Configuration class load a UserDetailsService and PasswordEncoder into the application context.

Full class here.

3. Add spring.factories

Next we need to tell Spring Boot that the class ​SecurityConfig is a candidate for auto-configuration. To do this we need to create a file named spring.factories which needs to be located under the META-INF (typically this is located src/main/resources, like this). Within spring.factories we will add the following:

Now when a developer brings in the my-org-starter-security, the class SecurityConfig will be automatically loaded into the application context.

Conditionals

Auto-configuration is great, but there might be components you want to load only in a certain scenarios. In our hypothetical organization we will have both web based and console based applications. How security works in these scenarios would differ dramatically, however using conditionals we can have only the appropriate classes loaded into the application context depending on the scenario, this saves developers who are using our starter from a lot of headaches.

Spring Boot provides a lot of flexibility when it comes to defining conditionals, however in this example we will use a couple of pre-defined ones @ConditionalOnWebApplication and @ConditionalOnNotWebApplication. Let’s create a couple classes that will only be auto-configured conditionally.

1. Create configuration classes

Like above we will create a couple of standard @Configuration classes, however we will also need to annotate those classes with the appropriate annotations, @ConditionalOnNotWebApplication for NonWebSecurityConfig and @ConditionalOnWebApplication for WebSecurityConfig. The classes look like this:

Note: Conditional annotations can also be placed at the method level if needed.

2. Update spring.factories

The spring.factories file will need to be updated with these new classes to mark them as auto-configuration candidates.

3. Curate Your POM

I will cover this in-depth in a separate article, but a key element in building a good starter, particularly when using @Conditional, is setting up the POM for your starter correctly. Commonly Conditionals will be looking for the presence (or non-presence) of classes on classpath. In my starter POM I made use of optionals, which made consuming my starter easier for console applications:

Metadata for Configuration

Inevitably some amount of configuration is needed. Remembering the exact name for a property or the correct values to supply it with can be difficult. To address this the Spring team provides a mechanism for defining metadata about configuration properties which can provide developers with which properties are available, information about those properties, and hints on valid values.

There are several options for supplying configuration metadata.

1. Add the spring-boot-configuration-processor:

Before you can do anything with metadata you will need to add the spring-boot-configuration-processor to your pom file for the metadata to be generated:

2. Add META-INF/spring-configuration-metadata.json

Under the META-INF folder where we earlier added spring.factories you will need to create another file called spring-configuration-metadata.json which, as its name suggests, Spring will read to generate configuration meta data.

With the initial setup work done, there are several ways to provide configuration metadata. Below at two popular ways of doing this.

Defining metadata in spring-configuration-metadata

Metadata can be defined directly within the spring-configuration-metadata file. Here I am providing metadata on how to configure console security:

With this added, going into application.properties hints are provided saying that my-org.cli.security.required-role is a property, providing information on that property, and then with the hints field, valid values and their meaning can also be provided. This metadata makes configuring security for console applications much easier for developers.

Binding Properties to a Class

Within a @Configuration class I could just use a @Value to retrieve the value of my-org.cli.security.required-role. However I can also bind that property to a class as well, this is particularly helpful when dealing with several properties. To do this simply create a POJO and add a @ConfigurationProperties annotation to it. You will also need to give it a prefix that is the same as the groups value you defined in spring-configuration-metadata, so in this case my-org.cli.security.

Supply Properties to a Configuration Class

If we want to retrieve the values out of CommandLineSecurityConfigurer we will need to annotate a @Configuration class with @EnableConfigurationProperties({ CommandLineSecurityConfigurer.class }), like I did in NonWebSecurityConfig.

Fields and Javadoc as Metadata

Class fields and Javadoc can also be used to provide metadata for properties. I created a second properties class, WebSecurityConfigurer. In this class I have three properties, userEndpoints, adminEndpoints, unsecuredEndpoints, each with Javadoc attached. Because I use the same group name for the prefix in @ConfigurationProperties as I supplied above in spring-configuration-metadata, Spring will process the fields and Javadoc and generate property metadata from it. As can be seen in the screen shot below the code. Like above, the values supplied in applications.properties will be bound to the fields.

Screen Shot 2019-12-30 at 11.31.52 AM

Providing Sensible Configuration Defaults

Another way Spring Boot is described as being “opinionated” is through providing “sensible defaults”. A common way this is experienced is through the default port number Spring Boot uses of 8080. Providing default property values is super easy, as I have done above by initializing unsecuredEndpoints with "/public**". ​

Conclusion

Starters offer a lot of opportunities for organizations to improve the experience of their developers. This article only scrapes the surface of what is possible. Be sure to check out the provided links for more details on how to create starters as well as the official documentation:

Auto-configuration: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-developing-auto-configuration

Providing metadata: https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-configuration-metadata.html

You can find the code example used in this article on my GitHub.

Wiring Multiple Datasources in a Spring Boot and Spring Data JPA Application

FifthElement

Having to wire a Spring Boot application to talk to multiple datasources is a requirement you come along every once awhile. The good news is this can be done reasonably easily within Spring Boot and in this article we will walk through how to accomplish this.

Wiring Multiple Datasources Step-by-Step

Pre-Requisites

To run the demo application the following tools are needed:

  • Docker
  • Java

There are also some shell scripts available for building, running, testing, and tearing down the application for *nix OS users.

Application Structure

The example application used in this article has two domain models, Doctor and Clinic, each of which are persisted to their own separate datastores. This application overall is a very standard implementation of Spring Boot and Spring Data. Because there is already a lot of great documentation on how to implement such an application, those steps will be skipped. However for clarity, here is what the overall structure of the application looks like:

com.developer.ibm.multidatasource\
   clinic\
      Clinic
      ClinicsController
      ClinicsDatasourceConfiguration
      ClinicsRepo
   doctor\
      Doctor
      DoctorsController
      DoctorsDatasourceConfiguration
      DoctorsRepo
   MultiDatasourceApplication

The full application can be seen here: https://github.com/wkorando/multi-datasources-spring-boot

Configuring Spring Data

The first step would be to define the @Configuration classes that add the DataSource, PlatformTransactionManager, and LocalContainerEntityManagerFactoryBean to the application context which will be used by Spring Data when communicating with the databases. Both of the configuration classes look essentially identical, with one exception which will be covered in detail below. Let’s step through some of the key elements in these configuration classes:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "clinicEntityManagerFactory", transactionManagerRef = "clinicTransactionManager")
public class ClinicsDatasourceConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "clinics.datasource")
    public DataSource clinicsDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    PlatformTransactionManager clinicTransactionManager(
            @Qualifier("clinicEntityManagerFactory") LocalContainerEntityManagerFactoryBean clinicEntityManagerFactory) {
        return new JpaTransactionManager(clinicEntityManagerFactory.getObject());
    }

    @Bean
    LocalContainerEntityManagerFactoryBean clinicEntityManagerFactory(
            @Qualifier("clinicsDataSource") DataSource clinicsDatasource, EntityManagerFactoryBuilder builder) {
        return builder.dataSource(clinicsDatasource).packages(Clinic.class).build();
    }
}

@Primary

Outside of name differences, the @Primary added to clinicsDataSource is the only functional difference between ClinicsDatasourceConfiguration and DoctorsDatasourceConfiguration. Adding @Primary to clinicsDataSource is necessary as some of the autoconfiguring behavior within Spring Data depends upon a DataSource being available in the application context. However in our situation there will be two DataSources, available in the application context so adding @Primary to one gives Spring the information it needs on which bean to choose. For this applications purposes, making clinicsDataSource the primary DataSource was an arbitrary decision. However deciding which DataSource should be the primary one might be something worth thinking about depending on the requirements and behavior of your application.

@ConfigurationProperties

Automatically maps the jdbc-url, password, and username properties prefixed with clinics.datasource available in the environment, in this case defined in application.properties (source), and maps them to the DataSource being created in clinicsDataSource. If this feels too “magical” DataSourceBuilder (javadoc) also has standard builder methods; url(String), password(String), username(String) available among others.

Using @ConfigurationProperties helps to keep the behavior, from a developer’s perspective, more consistent with how a Spring Boot application would work if it had only a single DataSource. @ConfigurationProperties could also be useful in other scenarios. Here is an example of using @ConfigurationProperties to map to fields within a configuration class from an earlier version of the example project.

@Qualifier

The arguments for the @Bean methods of clinicTransactionManager and clinicEntityManagerFactory are annotated with @Qualifier. Like with @Primary, @Qualifier tells the Spring which instance of class to use when there are multiple available in the application context. By default the name of a bean is the same name as the method that created the bean.

Defining the Properties

Next we need to provide Spring with the values to connect to both our databases. In this example we are connecting to containerized instances of MySQL and Postgres. We will go into a little more detail on this properties file below:

clinics.datasource.jdbc-url=jdbc:mysql://localhost:3306/clinics-db
clinics.datasource.username=root
clinics.datasource.password=secret

doctors.datasource.jdbc-url=jdbc:postgresql://localhost:5432/doctors-db
doctors.datasource.username=postgres
doctors.datasource.password=secret

spring.jpa.open-in-view=false

jdbc-url

When defining a datasource using the spring.datasource properties the property of url would be used. However with Spring Boot 2, HirkariDataSource became the standard DataSource implementation, so to use @ConfigurationProperties like in the @Configuration classes above, the property needs to be jdbc-url.

More info can be found here, as well as a workaround if you’d prefer to keep using url.

(I plan on going into more depth about this in a future article, as this change caused me a lot of pain while putting together the example application)

spring.jpa.open-in-view

This is ultimately an optional addition. By default Spring Boot sets this property to true which is arguably an anti-pattern, which you can read more about here. Why this is relevant in an article about configuring multiple datasources is that when spring.jpa.open-in-view is set to true, Spring MVC will look for an instance of PlatformTransactionManager and LocalContainerEntityManagerFactoryBean in the application context.

This could had been alternatively resolved by adding @Primary to one of the @Bean definitions of both PlatformTransactionManager and LocalContainerEntityManagerFactoryBean as was done with clinicsDataSource, however disabling spring.jpa.open-in-view should generally be done anyways, so that is a better resolution.

Running the Application

There are several scripts available for running the demo application, added for convenience and experimentation.

  • build.sh – builds the docker images and Java artifact
  • run.sh – starts up the Docker containers and Spring Boot application (note: there is a 15 second sleep between starting the containers and starting the app, to give the containers time to startup)
  • requests.sh – curl commands for POSTing and GETting to the Spring Boot application
  • kill.sh – stops and removes the Docker containers

The application by default runs at http://localhost:8080 with GET/POST endpoints residing at /clinics and /doctors.

Conclusion

With a little work a Spring Boot application can be setup to handle multiple datasources and still have an overall pretty familiar look and feel to it.

The code used in this article can be found here: https://github.com/wkorando/multi-datasources-spring-boot