Micronaut @ConfigurationBuilder Example
Micronaut is a modern, JVM-based framework designed for building lightweight, modular microservices. One of the key aspects of Micronaut is its powerful configuration system. Developers often need to map configuration properties from YAML or properties files into strongly typed Java classes. In our Micronaut @ConfigurationBuilder example we will see how configuration properties can be bound effectively in a Micronaut application.
1. What is Micronaut?
Micronaut is a modern, JVM-based, full-stack framework specifically designed for building modular, high-performance microservices, serverless applications, and cloud-native systems. It offers first-class support for Java, Kotlin, and Groovy, making it a flexible choice for JVM developers.
Unlike traditional Java frameworks that rely heavily on runtime reflection and proxies (such as Spring), Micronaut uses compile-time annotation processing for dependency injection, AOP (Aspect-Oriented Programming), and configuration. This unique approach drastically reduces memory usage and startup time, making it ideal for microservices and serverless environments like AWS Lambda or Google Cloud Functions.
Micronaut also integrates seamlessly with GraalVM to enable ahead-of-time (AOT) compilation into native executables, further improving performance and resource efficiency.
Key features of Micronaut include:
- Compile-time dependency injection: No reflection or runtime proxies, leading to better performance and smaller memory footprint.
- Fast startup time and low memory usage: Ideal for microservices and containerized environments.
- Seamless integration with GraalVM: Supports building native images for ultra-fast execution and minimal memory use.
- Reactive and non-blocking: Includes built-in support for reactive programming using RxJava, Project Reactor, etc.
- Built-in HTTP server/client: Lightweight Netty-based web server and declarative HTTP clients with easy configuration.
- Cloud-native readiness: Support for service discovery, distributed tracing, configuration management, and more.
- Modular architecture: Encourages separation of concerns and better maintainability.
1.1 What is @ConfigurationBuilder?
The @ConfigurationBuilder annotation in Micronaut is used to bind configuration properties from application.yml or application.properties to a builder-style class within a @ConfigurationProperties bean.
This is particularly useful when you want to populate a third-party class (like a builder) or a custom builder-style class without writing boilerplate setters. Instead of manually setting each field, Micronaut can automatically apply the configuration using compile-time processing, ensuring type safety and performance.
Key points include:
- Maps configuration values directly to a builder-style object, reducing the need for verbose setter methods.
- Must be used inside a class annotated with
@ConfigurationProperties, which defines the root property path. - Supports method prefixes like
set,with, or even custom-defined prefixes to match the builder pattern of the target class. - Helps in configuring complex objects like
NettyServerBuilder,OkHttpClient.Builder, or any custom builder you define in your codebase. - Improves maintainability by separating configuration from implementation logic.
1.1.1 Benefits of Using @ConfigurationBuilder
Using @ConfigurationBuilder provides several advantages in real-world applications:
- Integration with External Libraries: Easily bind configuration to builders from libraries like Netty, OkHttp, gRPC, etc.
- Cleaner Codebase: Reduces manual mapping and boilerplate code.
- Fluent Configuration: Works well with builder-style APIs and enhances readability.
- Compile-time safety: Errors are caught early, thanks to Micronaut’s annotation processing.
- Nested Config Support: Makes managing nested YAML structures intuitive.
1.1.2 Common Pitfalls and How to Avoid Them
While @ConfigurationBuilder is powerful, developers can run into a few issues:
- Method Naming Mismatch: Make sure your builder methods follow a consistent naming pattern like
setXyz()orwithXyz(). Use themethodsorprefixattributes of@ConfigurationBuilderif necessary. - Missing Default Constructor: The builder class should have a default constructor, as Micronaut instantiates it directly.
- Not Using @ConfigurationProperties: Remember that
@ConfigurationBuildermust be used within a class annotated with@ConfigurationProperties. - Incorrect Prefix in YAML: Double-check the YAML structure and prefix mapping. Mistyped keys can silently fail.
2. Code Example
Now that we understand the basics, let’s explore a real-world Micronaut ConfigurationBuilder example to see how configuration properties can be mapped from YAML into a builder-style Java class.
2.1 application.yml
app:
name: MicronautApp
retries: 5
pool:
max-connections: 10
min-connections: 2
This YAML file defines configuration values under the root namespace app. Nested inside is the pool section, which holds values to be mapped using a builder-style class.
2.2 AppConfig.java (Builder-style class)
package example;
public class AppConfig {
private int maxConnections;
private int minConnections;
public AppConfig setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
return this;
}
public AppConfig setMinConnections(int minConnections) {
this.minConnections = minConnections;
return this;
}
public int getMaxConnections() {
return maxConnections;
}
public int getMinConnections() {
return minConnections;
}
@Override
public String toString() {
return "AppConfig{" +
"maxConnections=" + maxConnections +
", minConnections=" + minConnections +
'}';
}
}
This builder-style class follows the fluent pattern with setXyz() methods that return this. Micronaut will use these setters to populate the fields based on the pool section from the YAML config.
2.3 MyConfig.java
package example;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.ConfigurationBuilder;
@ConfigurationProperties("app")
public class MyConfig {
private String name;
private int retries;
@ConfigurationBuilder(prefix = "pool")
private AppConfig appConfig = new AppConfig();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRetries() {
return retries;
}
public void setRetries(int retries) {
this.retries = retries;
}
public AppConfig getAppConfig() {
return appConfig;
}
}
This class is annotated with @ConfigurationProperties("app"), which tells Micronaut to bind properties under the app namespace. The @ConfigurationBuilder(prefix = "pool") annotation maps the nested pool properties to the AppConfig builder using the matching setter methods.
2.4 Application.java
package example;
import io.micronaut.runtime.Micronaut;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javax.annotation.PostConstruct;
@Singleton
public class Application {
@Inject
MyConfig myConfig;
@PostConstruct
public void run() {
System.out.println("App Name: " + myConfig.getName());
System.out.println("Retries: " + myConfig.getRetries());
System.out.println("AppConfig: " + myConfig.getAppConfig());
}
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
This simple application demonstrates how dependency injection is used to access MyConfig. At runtime, Micronaut injects the populated config bean, and we print out the values to verify that everything was correctly mapped — including the nested builder-style AppConfig object.
2.5 Code Output
App Name: MicronautApp
Retries: 5
AppConfig: AppConfig{maxConnections=10, minConnections=2}
As shown in the output, all configuration values are correctly mapped — including those inside the builder class. This example highlights how @ConfigurationBuilder enables a clean separation between configuration and logic while supporting third-party or fluent-style Java objects.
2.6 Unit Test
To ensure that the @ConfigurationBuilder annotation correctly binds properties from the configuration file into builder-style Java classes, we can write a simple unit test using JUnit 5.
In this test case, we simulate the values from application.yml using an in-memory configuration map and run a lightweight Micronaut ApplicationContext in test mode. This allows us to verify that the MyConfig class and its nested AppConfig instance are properly initialized.
The test validates:
- String and numeric property binding (
app.name,app.retries) - Builder-style binding using
@ConfigurationBuilderforapp.pool.* - That the correct values are injected into
AppConfigvia its setter-style methods
package example;
import io.micronaut.context.ApplicationContext;
import io.micronaut.context.env.Environment;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Map;
class MyConfigTest {
@Test
void testConfigurationBinding() {
// Mock configuration values (same as those in application.yml)
Map<String, Object> config = Map.of(
"app.name", "MicronautApp",
"app.retries", 5,
"app.pool.max-connections", 10,
"app.pool.min-connections", 2
);
try (ApplicationContext context = ApplicationContext.run(config, Environment.TEST)) {
MyConfig myConfig = context.getBean(MyConfig.class);
assertNotNull(myConfig);
assertEquals("MicronautApp", myConfig.getName());
assertEquals(5, myConfig.getRetries());
AppConfig appConfig = myConfig.getAppConfig();
assertNotNull(appConfig);
assertEquals(10, appConfig.getMaxConnections());
assertEquals(2, appConfig.getMinConnections());
}
}
}
If everything goes well, the test will pass successfully.
3. Conclusion
The @ConfigurationBuilder annotation in Micronaut simplifies the task of binding structured configuration to builder-style Java classes. It’s especially handy when working with existing libraries or third-party builders. As demonstrated, it allows for a clean separation of configuration logic and provides a type-safe way to use configuration in your Micronaut applications. By combining @ConfigurationProperties and @ConfigurationBuilder, developers can take full advantage of Micronaut’s powerful configuration binding features.



