Implementing Custom Starters for Spring Boot Applications
Spring Boot's auto-configuration mechanism allows for the creation of custom starter modules. These modules encapsulate configuration and dependency management, enabling rapid integration of shared libraries or internal frameworks without manual setup.
Auto-Configuration Fundamentals
Spring Boot scans for a file named spring.factories within the META-INF directory of the classpath during application startup. This file contains mappings that trigger specific configuration classes.
Example spring.factories snippet:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.SampleAutoConfiguration
Configuration classes are annotated with @Configuration and use conditional annotations to control their activation. For instance, @ConditionalOnClass checks for the presence of a specific class in the classpath.
Example Auto-Configuration Class:
@Configuration
@ConditionalOnClass(SampleService.class)
@EnableConfigurationProperties(SampleProperties.class)
public class SampleAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SampleService sampleService(SampleProperties properties) {
return new SampleService(properties);
}
}
Externalizing Configuration Properties
Properties can be externalized to application.properties or application.yml. A properties class annotated with @ConfigurationProperties binds these external values.
Example Properties Class:
@ConfigurationProperties(prefix = "app.sample")
public class SampleProperties {
private String endpoint;
private int timeout = 5000;
// Standard getters and setters
}
Corresponding application.properties:
app.sample.endpoint=https://api.example.com
app.sample.timeout=3000
Building a Custom Starter
A typical custom starter consists of two modules:
- A autoconfigure module containing the configuration classes and property bindings.
- A starter module that aggregates dependencies, including the autoconfigure module and any required libraries.
1. Autoconfigure Module Structure
Define a configuration properties class.
@ConfigurationProperties(prefix = "custom.greeting")
public class GreetingConfigProperties {
private String recipient = "User";
private String message = "Hello";
// Getters and setters
}
Create the main auto-configuration class.
@Configuration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingConfigProperties.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public GreetingService greetingService(GreetingConfigProperties props) {
return new GreetingService(props.getRecipient(), props.getMessage());
}
}
Register the auto-configuration class in /META-INF/spring.factories.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.GreetingAutoConfiguration
2. Starter Module Depandencies (pom.xml)
The starter's POM file declares dependencies on the autoconfigure module and the core library.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>greeting-spring-boot-starter</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-core</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
Using the Custom Starter
Add the starter dependency to a Spring Boot application's pom.xml.
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
Configure properties in application.properties.
custom.greeting.recipient=Developer
custom.greeting.message=Welcome
Inject and use the auto-configured bean.
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private GreetingService greetingService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) {
System.out.println(greetingService.generateGreeting());
}
}
The @ConditionalOnMissingBean annotation provides flexibility, allowing developers to override the auto-configured bean by defining they own GreetingService bean in their application context.