4.3 Configuration Injection
You can inject configuration values into beans with Micronaut using the @Value annotation.
Using the @Value
Annotation
Consider the following example:
@Value Example
import javax.inject.Singleton;
@Singleton
public class EngineImpl implements Engine {
@Value("${my.engine.cylinders:6}") (1)
protected int cylinders;
@Override
public int getCylinders() {
return this.cylinders;
}
public String start() {(2)
return "Starting V" + getCylinders() + " Engine";
}
}
@Value Example
import javax.inject.Singleton
@Singleton
class EngineImpl implements Engine {
@Value('${my.engine.cylinders:6}') (1)
protected int cylinders
@Override
int getCylinders() {
this.cylinders
}
String start() { (2)
"Starting V${cylinders} Engine"
}
}
@Value Example
import javax.inject.Singleton
@Singleton
class EngineImpl : Engine {
@Value("\${my.engine.cylinders:6}") (1)
override var cylinders: Int = 0
protected set
override fun start(): String {(2)
return "Starting V$cylinders Engine"
}
}
1 | The @Value annotation accepts a string that can have embedded placeholder values (the default value can be provided by specifying a value after the colon : character). |
2 | The injected value can then be used within code. |
Note that @Value
can also be used to inject a static value, for example the following will inject the number 10:
Static @Value Example
@Value("10")
int number;
However it is definitely more useful when used to compose injected values combining static content and placeholders. For example to setup a URL:
Placeholders with @Value
@Value("http://${my.host}:${my.port}")
URL url;
In the above example the URL is constructed from 2 placeholder properties that must be present in configuration: my.host
and my.port
.
Remember that to specify a default value in a placeholder expression, you should use the colon :
character, however if the default you are trying to specify has a colon then you should escape the value with back ticks. For example:
Placeholders with @Value
@Value("${my.url:`http://foo.com`}")
URL url;
Note that there is nothing special about @Value
itself regarding the resolution of property value placeholders.
Due to Micronaut’s extensive support for annotation metadata you can in fact use property placeholder expressions on any annotation. For example, to make the path of a @Controller
configurable you can do:
@Controller("${hello.controller.path:/hello}")
class HelloController {
...
}
In the above case if hello.controller.path
is specified in configuration then the controller will be mapped to the path specified otherwise it will be mapped to /hello
.
You can also make the target server for @Client configurable (although service discovery approaches are often better), for example:
@Client("${my.server.url:`http://localhost:8080`}")
interface HelloClient {
...
}
In the above example the property my.server.url
can be used to configure the client otherwise the client will fallback to a localhost address.
Using the @Property
Annotation
Recall that the @Value annotation receives a String value which is a mix of static content and placeholder expressions. This can lead to confusion if you attempt to do the following:
Incorrect usage of @Value
@Value("my.url")
String url;
In the above case the value my.url
will be injected and set to the url
field and not the value of the my.url
property from your application configuration, this is because @Value
only resolves placeholders within the value specified to it.
If you wish to inject a specific property name then you may be better off using @Property:
Using @Property
import io.micronaut.context.annotation.Property;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class Engine {
@Property(name = "my.engine.cylinders") (1)
protected int cylinders; (2)
private String manufacturer;
public int getCylinders() {
return cylinders;
}
public String getManufacturer() {
return manufacturer;
}
@Inject
public void setManufacturer(@Property(name = "my.engine.manufacturer") String manufacturer) { (3)
this.manufacturer = manufacturer;
}
}
Using @Property
import io.micronaut.context.annotation.Property
import javax.inject.Singleton
@Singleton
class Engine {
@Property(name = "my.engine.cylinders") (1)
protected int cylinders (2)
@Property(name = "my.engine.manufacturer") (3)
String manufacturer
int getCylinders() {
cylinders
}
}
Using @Property
import io.micronaut.context.annotation.Property
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class Engine {
@field:Property(name = "my.engine.cylinders") (1)
protected var cylinders: Int = 0 (2)
@set:Inject
@setparam:Property(name = "my.engine.manufacturer") (3)
var manufacturer: String? = null
fun cylinders(): Int {
return cylinders
}
}
1 | The my.engine.cylinders property will be resolved from configuration and injected directly into the field. |
2 | Fields subjected to injection should never be private otherwise expensive reflection must be used |
3 | The @Property annotation is used to inject through the setter |
Because it is not possible to define a default value with @Property , if the value doesn’t exist or cannot be converted to the required type, the bean instantiation will fail. |
The above will instead inject the value of the my.url
property resolved from application configuration. If the property can not be found in configuration, an exception will be thrown. As with other types of injection, the injection point can also be annotated with @Nullable
to make the injection optional.
You can also use this feature to resolve sub maps. For example, consider the following configuration:
Example application.yml
configuration
datasources:
default:
name: 'mydb'
jpa:
default:
properties:
hibernate:
hbm2ddl:
auto: update
show_sql: true
If you wish to resolve a flattened map containing only the properties starting with hibernate
then you can do so with @Property
, for example:
Using @Property
@Property(name = "jpa.default.properties")
Map<String, String> jpaProperties;
The injected map will contain the keys hibernate.hbm2ddl.auto
and hibernate.show_sql
and their values.
The @MapFormat annotation can be used to customize the injected map depending whether you want nested keys, flat keys and it allows customization of the key style via the StringConvention enum. |