Annotation Interface Value


@Retention(RUNTIME) @Target({METHOD,FIELD,PARAMETER}) public @interface Value
Annotation used to customize behaviour of JavaBean deserialization support.

The first option for generic Config to JavaBean deserialization works just with class with no-parameter constructor. Each JavaBean property value is then set by value mapped from appropriate configuration node. Each public setter method and public non-final fields are taken as JavaBean properties that will be set. The deserialization process is applied recursively on each property.

Use Transient annotation to exclude setter or field from set of processed JavaBean properties.

By default JavaBean property name is used as config key to get configuration node. The config key can be customized by key() attribute. Use the annotation on public setter or public field. Annotation on method has precedence over annotation used on field. The second one is ignored.

If the appropriate configuration node does not exist it is possible to specify default value:

  • withDefaultSupplier() - instance of supplier class is used to get default value of target type; or
  • withDefault() - default value in String form that will be mapped to target type by associated config mapping function
In case of both default attributes are set the withDefaultSupplier is used and withDefault is ignored.

 public class AppConfig {
     private String greeting;
     private int pageSize;
     private List<Integer> range;

     public AppConfig() { // <1>
     }

     public void setGreeting(String greeting) { // <2>
         this.greeting = greeting;
     }

     @Value(key = "page-size", withDefault = "10") // <3>
     public void setPageSize(int pageSize) {
         this.pageSize = pageSize;
     }

     @Value(withDefaultSupplier = DefaultRangeSupplier.class) // <4>
     public void setRange(List<Integer> basicRange) {
         this.range = basicRange;
     }

     //...

     public static class DefaultRangeSupplier // <5>
                 implements Supplier<List<Integer>> {
         @Override
         public List<Integer> get() {
             return List.of(0, 10);
         }
     }
 }
 
  1. public no-parameter constructor;
  2. property greeting is not customized; will be set from config node with greeting key, if exists;
  3. property pageSize customizes key of config node to page-size; if the config node does not exist, value "10" will be mapped to int;
  4. property range will be set from config node with same range key; if the config node does not exist, DefaultRangeSupplier instance will be used to get default value;
  5. DefaultRangeSupplier is used to supply List<Integer> value.

The second option is to provide factory public static method from with parameters set from configuration. Or public "factory" constructor with parameters can be used too.


 public class AppConfig {
     private final String greeting;
     private final int pageSize;
     private final List<Integer> basicRange;

     private AppConfig(String greeting, int pageSize, List<Integer> basicRange) {
         this.greeting = greeting;
         this.pageSize = pageSize;
         this.basicRange = basicRange;
     }

     //...

     // FACTORY METHOD
     public static AppConfig create(@Value(key = "greeting", withDefault = "Hi")
                                  String greeting,
                                  @Value(key = "page-size", withDefault = "10")
                                  int pageSize,
                                  @Value(key = "basic-range",
                                          withDefaultSupplier = DefaultBasicRangeSupplier.class)
                                  List<Integer> basicRange) {
         return new AppConfig(greeting, pageSize, basicRange);
     }
 }
 

The third option is to provide Builder accessible by public static builder() method. The Builder instances is initialized via public setters or fields, similar to the first deserialization option. Finally, Builder has build() method that creates new instances of a bean.


 public class AppConfig {
     private final String greeting;
     private final int pageSize;
     private final List<Integer> basicRange;

     private AppConfig(String greeting, int pageSize, List<Integer> basicRange) {
         this.greeting = greeting;
         this.pageSize = pageSize;
         this.basicRange = basicRange;
     }

     // BUILDER METHOD
     public static Builder builder() {
         return new Builder();
     }

     public static class Builder {
         private String greeting;
         private int pageSize;
         private List<Integer> basicRange;

         private Builder() {
         }

         @Value(withDefault = "Hi")
         public void setGreeting(String greeting) {
             this.greeting = greeting;
         }

         @Value(key = "page-size", withDefault = "10")
         public void setPageSize(int pageSize) {
             this.pageSize = pageSize;
         }

         @Value(key = "basic-range",
                 withDefaultSupplier = DefaultBasicRangeSupplier.class)
         public void setBasicRange(List<Integer> basicRange) {
             this.basicRange = basicRange;
         }

         // BUILD METHOD
         public AppConfig build() {
             return new AppConfig(greeting, pageSize, basicRange);
         }
     }
 }
 

Configuration example:


 {
     "app": {
         "greeting": "Hello",
         "page-size": 20,
         "range": [ -20, 20 ]
     }
 }
 
Getting app config node as AppConfig instance:

 AppConfig appConfig = config.get("app").as(AppConfig.class);
 assert appConfig.getGreeting().equals("Hello");
 assert appConfig.getPageSize() == 20;
 assert appConfig.getRange().get(0) == -20;
 assert appConfig.getRange().get(1) == 20;
 
In this case default values where not used because JSON contains all expected nodes.

The annotation cannot be applied on same JavaBean property together with Transient.

See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
    Class that represents not-set default values.
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    Specifies a key of configuration node to be used to set JavaBean property value from.
    Specifies default value in form of single String value that will be used to set JavaBean property value in case configuration does not contain a config node of appropriate config key.
    Class<? extends Supplier<?>>
    Specifies supplier of default value that will be used to set JavaBean property value in case configuration does not contain config node of appropriate config key.
  • Element Details

    • key

      String key
      Specifies a key of configuration node to be used to set JavaBean property value from.

      If not specified original JavaBean property name is used.

      Returns:
      config property key
      Default:
      ""
    • withDefault

      String withDefault
      Specifies default value in form of single String value that will be used to set JavaBean property value in case configuration does not contain a config node of appropriate config key.

      In case withDefaultSupplier() is also used current value is ignored.

      Returns:
      single default value that will be converted into target type
      Default:
      "io.helidon.config:default=null"
    • withDefaultSupplier

      Class<? extends Supplier<?>> withDefaultSupplier
      Specifies supplier of default value that will be used to set JavaBean property value in case configuration does not contain config node of appropriate config key.

      Default value is used in case appropriate config value is not set. In case withDefault() is also used this one has higher priority and will be used.

      Returns:
      supplier that will provide default value in target type
      Default:
      io.helidon.config.objectmapping.Value.None.class