4.4 Configuration Properties
You can create type safe configuration by creating classes that are annotated with @ConfigurationProperties.
Micronaut will produce a reflection-free @ConfigurationProperties
bean and will also at compile time calculate the property paths to evaluate, greatly improving the speed and efficiency of loading @ConfigurationProperties
.
An example of a configuration class can be seen below:
@ConfigurationProperties Example
import io.micronaut.context.annotation.ConfigurationProperties;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
@ConfigurationProperties("my.engine") (1)
public class EngineConfig {
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public int getCylinders() {
return cylinders;
}
public void setCylinders(int cylinders) {
this.cylinders = cylinders;
}
public CrankShaft getCrankShaft() {
return crankShaft;
}
public void setCrankShaft(CrankShaft crankShaft) {
this.crankShaft = crankShaft;
}
@NotBlank (2)
private String manufacturer = "Ford"; (3)
@Min(1L)
private int cylinders;
private CrankShaft crankShaft = new CrankShaft();
@ConfigurationProperties("crank-shaft")
public static class CrankShaft { (4)
public Optional<Double> getRodLength() {
return rodLength;
}
public void setRodLength(Optional<Double> rodLength) {
this.rodLength = rodLength;
}
private Optional<Double> rodLength = Optional.empty(); (5)
}
}
@ConfigurationProperties Example
import io.micronaut.context.annotation.ConfigurationProperties
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
@ConfigurationProperties('my.engine') (1)
class EngineConfig {
@NotBlank (2)
String manufacturer = "Ford" (3)
@Min(1L)
int cylinders
CrankShaft crankShaft = new CrankShaft()
@ConfigurationProperties('crank-shaft')
static class CrankShaft { (4)
Optional<Double> rodLength = Optional.empty() (5)
}
}
@ConfigurationProperties Example
import io.micronaut.context.annotation.ConfigurationProperties
import java.util.*
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
@ConfigurationProperties("my.engine") (1)
class EngineConfig {
@NotBlank (2)
var manufacturer = "Ford" (3)
@Min(1L)
var cylinders: Int = 0
var crankShaft = CrankShaft()
@ConfigurationProperties("crank-shaft")
class CrankShaft { (4)
var rodLength: Optional<Double> = Optional.empty() (5)
}
}
1 | The @ConfigurationProperties annotation takes the configuration prefix |
2 | You can use javax.validation to validate the configuration |
3 | Default values can be assigned to the property |
4 | Static inner classes can provided nested configuration |
5 | Optional configuration values can be wrapped in a java.util.Optional |
Once you have prepared a type safe configuration it can simply be injected into your objects like any other bean:
@ConfigurationProperties Dependency Injection
@Singleton
public class EngineImpl implements Engine {
private final EngineConfig config;
public EngineImpl(EngineConfig config) {(1)
this.config = config;
}
@Override
public int getCylinders() {
return config.getCylinders();
}
public String start() {(2)
return getConfig().getManufacturer() + " Engine Starting V" + getConfig().getCylinders() +
" [rodLength=" + getConfig().getCrankShaft().getRodLength().orElse(6.0d) + "]";
}
public final EngineConfig getConfig() {
return config;
}
}
@ConfigurationProperties Dependency Injection
@Singleton
class EngineImpl implements Engine {
final EngineConfig config
EngineImpl(EngineConfig config) { (1)
this.config = config
}
@Override
int getCylinders() {
config.cylinders
}
String start() { (2)
"${config.manufacturer} Engine Starting V${config.cylinders} [rodLength=${config.crankShaft.rodLength.orElse(6.0d)}]"
}
}
@ConfigurationProperties Dependency Injection
@Singleton
class EngineImpl(val config: EngineConfig) : Engine {(1)
override val cylinders: Int
get() = config.cylinders
override fun start(): String {(2)
return "${config.manufacturer} Engine Starting V${config.cylinders} [rodLength=${config.crankShaft.rodLength.orElse(6.0)}]"
}
}
1 | Inject the EngineConfig bean |
2 | Use the configuration properties |
Configuration values can then be supplied from one of the PropertySource instances. For example:
Supply Configuration
LinkedHashMap<String, Object> map = new LinkedHashMap(1);
map.put("my.engine.cylinders", "8");
ApplicationContext applicationContext = ApplicationContext.run(map, "test");
Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
Supply Configuration
ApplicationContext applicationContext = ApplicationContext.run(
['my.engine.cylinders': '8'],
"test"
)
Vehicle vehicle = applicationContext
.getBean(Vehicle)
println(vehicle.start())
Supply Configuration
val map = mapOf( "my.engine.cylinders" to "8")
val applicationContext = ApplicationContext.run(map, "test")
val vehicle = applicationContext.getBean(Vehicle::class.java)
println(vehicle.start())
The above example prints: "Ford Engine Starting V8 [rodLength=6.0]"
Note for more complex configurations you can structure @ConfigurationProperties beans through inheritance.
For example creating a subclass of EngineConfig
with @ConfigurationProperties('bar')
will resolve all properties under the path my.engine.bar
.