MapFormat

For some use cases it may be desirable to accept a map of arbitrary configuration properties that can be supplied to a bean, especially if the bean represents a third-party API where not all of the possible configuration properties are known by the developer. For example, a datasource may accept a map of configuration properties specific to a particular database driver, allowing the user to specify any desired options in the map without coding every single property explicitly.

For this purpose, the MapFormat annotation allows you to bind a map to a single configuration property, and specify whether to accept a flat map of keys to values, or a nested map (where the values may be additional maps].

@MapFormat Example

  1. import io.micronaut.context.annotation.ConfigurationProperties;
  2. import javax.validation.constraints.Min;
  3. import java.util.Map;
  4. import io.micronaut.core.convert.format.MapFormat;
  5. @ConfigurationProperties("my.engine")
  6. public class EngineConfig {
  7. public int getCylinders() {
  8. return cylinders;
  9. }
  10. public void setCylinders(int cylinders) {
  11. this.cylinders = cylinders;
  12. }
  13. public Map<Integer, String> getSensors() {
  14. return sensors;
  15. }
  16. public void setSensors(Map<Integer, String> sensors) {
  17. this.sensors = sensors;
  18. }
  19. @Min(1L)
  20. private int cylinders;
  21. @MapFormat(transformation = MapFormat.MapTransformation.FLAT) (1)
  22. private Map<Integer, String> sensors;
  23. }

@MapFormat Example

  1. import io.micronaut.context.annotation.ConfigurationProperties
  2. import javax.validation.constraints.Min
  3. import io.micronaut.core.convert.format.MapFormat
  4. @ConfigurationProperties('my.engine')
  5. class EngineConfig {
  6. @Min(1L)
  7. int cylinders
  8. @MapFormat(transformation = MapFormat.MapTransformation.FLAT) (1)
  9. Map<Integer, String> sensors
  10. }

@MapFormat Example

  1. import io.micronaut.context.annotation.ConfigurationProperties
  2. import javax.validation.constraints.Min
  3. import io.micronaut.core.convert.format.MapFormat
  4. @ConfigurationProperties("my.engine")
  5. class EngineConfig {
  6. @Min(1L)
  7. var cylinders: Int = 0
  8. @MapFormat(transformation = MapFormat.MapTransformation.FLAT) (1)
  9. var sensors: Map<Int, String>? = null
  10. }
1Note the transformation argument to the annotation; possible values are MapTransformation.FLAT (for flat maps) and MapTransformation.NESTED (for nested maps)

EngineImpl

  1. @Singleton
  2. public class EngineImpl implements Engine {
  3. @Override
  4. public Map getSensors() {
  5. return config.getSensors();
  6. }
  7. @Override
  8. public String start() {
  9. return "Engine Starting V" + getConfig().getCylinders() + " [sensors=" + getSensors().size() + "]";
  10. }
  11. public EngineConfig getConfig() {
  12. return config;
  13. }
  14. public void setConfig(EngineConfig config) {
  15. this.config = config;
  16. }
  17. @Inject
  18. private EngineConfig config;
  19. }

EngineImpl

  1. @Singleton
  2. class EngineImpl implements Engine {
  3. @Inject EngineConfig config
  4. @Override
  5. Map getSensors() {
  6. config.sensors
  7. }
  8. @Override
  9. String start() {
  10. "Engine Starting V${config.cylinders} [sensors=${sensors.size()}]"
  11. }
  12. }

EngineImpl

  1. @Singleton
  2. class EngineImpl : Engine {
  3. override val sensors: Map<*, *>?
  4. get() = config!!.sensors
  5. @Inject
  6. var config: EngineConfig? = null
  7. override fun start(): String {
  8. return "Engine Starting V${config!!.cylinders} [sensors=${sensors!!.size}]"
  9. }
  10. }

Now a map of properties can be supplied to the my.engine.sensors configuration property.

Use Map Configuration

  1. LinkedHashMap<String, Object> map = new LinkedHashMap(2);
  2. map.put("my.engine.cylinders", "8");
  3. LinkedHashMap<Integer, String> map1 = new LinkedHashMap(2);
  4. map1.put(0, "thermostat");
  5. map1.put(1, "fuel pressure");
  6. map.put("my.engine.sensors", map1);
  7. ApplicationContext applicationContext = ApplicationContext.run(map, "test");
  8. Vehicle vehicle = applicationContext.getBean(Vehicle.class);
  9. DefaultGroovyMethods.println(this, vehicle.start());

Use Map Configuration

  1. ApplicationContext applicationContext = ApplicationContext.run(
  2. ['my.engine.cylinders': '8', 'my.engine.sensors': [0: 'thermostat', 1: 'fuel pressure']],
  3. "test"
  4. )
  5. Vehicle vehicle = applicationContext
  6. .getBean(Vehicle)
  7. println(vehicle.start())

Use Map Configuration

  1. val subMap = mapOf(
  2. 0 to "thermostat",
  3. 1 to "fuel pressure"
  4. )
  5. val map = mapOf(
  6. "my.engine.cylinders" to "8",
  7. "my.engine.sensors" to subMap
  8. )
  9. val applicationContext = ApplicationContext.run(map, "test")
  10. val vehicle = applicationContext.getBean(Vehicle::class.java)
  11. DefaultGroovyMethods.println(this, vehicle.start())

The above example prints: "Engine Starting V8 [sensors=2]"