4.3 Configuration Injection

You can inject configuration values into beans using the @Value annotation.

Using the @Value Annotation

Consider the following example:

@Value Example

  1. import io.micronaut.context.annotation.Value;
  2. import jakarta.inject.Singleton;
  3. @Singleton
  4. public class EngineImpl implements Engine {
  5. @Value("${my.engine.cylinders:6}") (1)
  6. protected int cylinders;
  7. @Override
  8. public int getCylinders() {
  9. return cylinders;
  10. }
  11. @Override
  12. public String start() {(2)
  13. return "Starting V" + getCylinders() + " Engine";
  14. }
  15. }

@Value Example

  1. import io.micronaut.context.annotation.Value
  2. import jakarta.inject.Singleton
  3. @Singleton
  4. class EngineImpl implements Engine {
  5. @Value('${my.engine.cylinders:6}') (1)
  6. protected int cylinders
  7. @Override
  8. int getCylinders() {
  9. cylinders
  10. }
  11. @Override
  12. String start() { (2)
  13. "Starting V$cylinders Engine"
  14. }
  15. }

@Value Example

  1. import io.micronaut.context.annotation.Value
  2. import jakarta.inject.Singleton
  3. @Singleton
  4. class EngineImpl : Engine {
  5. @Value("\${my.engine.cylinders:6}") (1)
  6. override var cylinders: Int = 0
  7. protected set
  8. override fun start(): String { (2)
  9. return "Starting V$cylinders Engine"
  10. }
  11. }
1The @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).
2The injected value can then be used within code.

Note that @Value can also be used to inject a static value. For example the following injects the number 10:

Static @Value Example

  1. @Value("10")
  2. int number;

This is even more useful when used to compose injected values combining static content and placeholders. For example to set up a URL:

Placeholders with @Value

  1. @Value("http://${my.host}:${my.port}")
  2. URL url;

In the above example the URL is constructed from two 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 use the colon : character. However, if the default you specify includes a colon, you must escape the value with backticks. For example:

Placeholders with @Value

  1. @Value("${my.url:`http://foo.com`}")
  2. 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 use property placeholder expressions on any annotation. For example, to make the path of a @Controller configurable you can do:

  1. @Controller("${hello.controller.path:/hello}")
  2. class HelloController {
  3. ...
  4. }

In the above case, if hello.controller.path is specified in configuration the controller will be mapped to the specified path, 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:

  1. @Client("${my.server.url:`http://localhost:8080`}")
  2. interface HelloClient {
  3. ...
  4. }

In the above example the property my.server.url can be used to configure the client, otherwise the client falls back to a localhost address.

Using the @Property Annotation

Recall that the @Value annotation receives a String value which can be a mix of static content and placeholder expressions. This can lead to confusion if you attempt to do the following:

Incorrect usage of @Value

  1. @Value("my.url")
  2. String url;

In the above case the literal string value my.url is 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.

To inject a specific property name, you may be better off using @Property:

Using @Property

  1. import io.micronaut.context.annotation.Property;
  2. import jakarta.inject.Inject;
  3. import jakarta.inject.Singleton;
  4. @Singleton
  5. public class Engine {
  6. @Property(name = "my.engine.cylinders") (1)
  7. protected int cylinders; (2)
  8. private String manufacturer;
  9. public int getCylinders() {
  10. return cylinders;
  11. }
  12. public String getManufacturer() {
  13. return manufacturer;
  14. }
  15. @Inject
  16. public void setManufacturer(@Property(name = "my.engine.manufacturer") String manufacturer) { (3)
  17. this.manufacturer = manufacturer;
  18. }
  19. }

Using @Property

  1. import io.micronaut.context.annotation.Property
  2. import jakarta.inject.Singleton
  3. @Singleton
  4. class Engine {
  5. @Property(name = "my.engine.cylinders") (1)
  6. protected int cylinders (2)
  7. @Property(name = "my.engine.manufacturer") (3)
  8. String manufacturer
  9. int getCylinders() {
  10. cylinders
  11. }
  12. }

Using @Property

  1. import io.micronaut.context.annotation.Property
  2. import jakarta.inject.Inject
  3. import jakarta.inject.Singleton
  4. @Singleton
  5. class Engine {
  6. @field:Property(name = "my.engine.cylinders") (1)
  7. protected var cylinders: Int = 0 (2)
  8. @set:Inject
  9. @setparam:Property(name = "my.engine.manufacturer") (3)
  10. var manufacturer: String? = null
  11. fun cylinders(): Int {
  12. return cylinders
  13. }
  14. }
1The my.engine.cylinders property is resolved from configuration and injected into the field.
2Fields subject to injection should not be private because expensive reflection must be used
3The @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, bean instantiation will fail.

The above instead injects the value of the my.url property resolved from application configuration. If the property cannot be found in configuration, an exception is 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

  1. datasources:
  2. default:
  3. name: 'mydb'
  4. jpa:
  5. default:
  6. properties:
  7. hibernate:
  8. hbm2ddl:
  9. auto: update
  10. show_sql: true

To resolve a flattened map containing only the properties starting with hibernate, use @Property, for example:

Using @Property

  1. @Property(name = "jpa.default.properties")
  2. 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 on whether you want nested keys or flat keys, and it allows customization of the key style via the StringConvention enum.