@@ -894,7 +894,17 @@ Using the `@Value("${property}")` annotation to inject configuration properties
894894sometimes be cumbersome, especially if you are working with multiple properties or your
895895data is hierarchical in nature. Spring Boot provides an alternative method of working
896896with properties that lets strongly typed beans govern and validate the configuration of
897- your application, as shown in the following example:
897+ your application.
898+
899+ TIP: See also the <<boot-features-external-config-vs-value,differences between `@Value`
900+ and type-safe configuration properties>>.
901+
902+
903+
904+ [[boot-features-external-config-java-bean-binding]]
905+ ==== JavaBean properties binding
906+ It is possible to bind a bean declaring standard JavaBean properties as shown in the
907+ following example:
898908
899909[source,java,indent=0]
900910----
@@ -958,13 +968,13 @@ The preceding POJO defines the following properties:
958968the name of the property. In particular, the return type is not used at all there and
959969could have been `SecurityProperties`.
960970* `acme.security.password`.
961- * `acme.security.roles`, with a collection of `String`.
971+ * `acme.security.roles`, with a collection of `String` that defaults to `USER` .
962972
963973[NOTE]
964974====
965- Getters and setters are usually mandatory, since binding is through standard Java Beans
966- property descriptors, just like in Spring MVC. A setter may be omitted in the following
967- cases:
975+ Such arrangement relies on a default empty constructor and getters and setters are usually
976+ mandatory, since binding is through standard Java Beans property descriptors, just like in
977+ Spring MVC. A setter may be omitted in the following cases:
968978
969979* Maps, as long as they are initialized, need a getter but not necessarily a setter,
970980since they can be mutated by the binder.
@@ -984,11 +994,84 @@ Finally, only standard Java Bean properties are considered and binding on static
984994properties is not supported.
985995====
986996
987- TIP: See also the <<boot-features-external-config-vs-value,differences between `@Value`
988- and `@ConfigurationProperties`>>.
989997
990- You also need to list the properties classes to register in the
991- `@EnableConfigurationProperties` annotation, as shown in the following example:
998+
999+ [[boot-features-external-config-constructor-binding]]
1000+ ==== Constructor binding
1001+ The example in the previous section can be rewritten in an immutable fashion as shown in
1002+ the following example:
1003+
1004+ [source,java,indent=0]
1005+ ----
1006+ package com.example;
1007+
1008+ import java.net.InetAddress;
1009+ import java.util.List;
1010+
1011+ import org.springframework.boot.context.properties.ConfigurationProperties;
1012+ import org.springframework.boot.context.properties.ConfigurationPropertyDefaultValue;
1013+
1014+ @ConfigurationProperties("acme")
1015+ public class AcmeProperties {
1016+
1017+ private final boolean enabled;
1018+
1019+ private final InetAddress remoteAddress;
1020+
1021+ private final Security security;
1022+
1023+ public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
1024+ this.enabled = enabled;
1025+ this.remoteAddress = remoteAddress;
1026+ this.security = security;
1027+ }
1028+
1029+ public boolean isEnabled() { ... }
1030+
1031+ public InetAddress getRemoteAddress() { ... }
1032+
1033+ public Security getSecurity() { ... }
1034+
1035+ public static class Security {
1036+
1037+ private final String username;
1038+
1039+ private final String password;
1040+
1041+ private final List<String> roles;
1042+
1043+ public Security(String username, String password,
1044+ @ConfigurationPropertyDefaultValue("USER") List<String> roles) {
1045+ this.username = username;
1046+ this.password = password;
1047+ this.roles = roles;
1048+ }
1049+
1050+ public String getUsername() { ... }
1051+
1052+ public String getPassword() { ... }
1053+
1054+ public List<String> getRoles() { ... }
1055+
1056+ }
1057+
1058+ }
1059+ ----
1060+
1061+ In this setup one, and only one constructor must be defined with the list of properties
1062+ that you wish to bind and not other properties than the ones in the constructor are bound.
1063+
1064+ Default values can be specified using `@ConfigurationPropertyDefaultValue` and the same
1065+ conversion service will be applied to coerce the `String` value to the target type of a
1066+ missing property.
1067+
1068+
1069+
1070+ [[boot-features-external-config-enabling]]
1071+ ==== Enabling `@ConfigurationProperties`-annotated types
1072+ Spring Boot provides an infrastructure to bind such types and register them as beans
1073+ automatically. Any `@Configuration` class can specify the list of types to process as
1074+ shown in the following example:
9921075
9931076[source,java,indent=0]
9941077----
@@ -1006,15 +1089,19 @@ specified in the `@ConfigurationProperties` annotation and `<fqn>` is the fully
10061089name of the bean. If the annotation does not provide any prefix, only the fully qualified
10071090name of the bean is used.
10081091
1009- The bean name in the example above is `acme-com.example.AcmeProperties`.
1092+ The bean name in the examples above is `acme-com.example.AcmeProperties`.
10101093====
10111094
1012- The preceding configuration creates a regular bean for `AcmeProperties`. We recommend that
1013- `@ConfigurationProperties` only deal with the environment and, in particular, does not
1014- inject other beans from the context. Keep in mind that the
1015- `@EnableConfigurationProperties` annotation is _also_ automatically applied to your
1016- project so that any _existing_ bean annotated with `@ConfigurationProperties` is
1017- configured from the `Environment`. Instead of annotating `MyConfiguration` with
1095+ We recommend that `@ConfigurationProperties` only deal with the environment and, in
1096+ particular, does not inject other beans from the context. In particular, it is not
1097+ possible to inject other beans using the constructor as this would trigger the constructor
1098+ binder that only deals with the environment.
1099+
1100+ For corner cases, setter injection can be used or any of the `*Aware` interfaces provided
1101+ by the framework (such as `EnvironmentAware` if you need access to the `Environment`).
1102+
1103+ If you find using `@EnableConfigurationProperties` tedious, you can also declare a bean
1104+ yourself. For instance, instead of annotating `MyConfiguration` with
10181105`@EnableConfigurationProperties(AcmeProperties.class)`, you could make `AcmeProperties`
10191106a bean, as shown in the following example:
10201107
@@ -1024,11 +1111,21 @@ a bean, as shown in the following example:
10241111 @ConfigurationProperties(prefix="acme")
10251112 public class AcmeProperties {
10261113
1027- // ... see the preceding example
1114+ private boolean enabled;
1115+
1116+ private InetAddress remoteAddress;
1117+
1118+ private final Security security = new Security();
1119+
1120+ // ... see the preceding JavaBean properties binding example
10281121
10291122 }
10301123----
10311124
1125+
1126+
1127+ [[boot-features-external-config-using]]
1128+ ==== Using `@ConfigurationProperties`-annotated types
10321129This style of configuration works particularly well with the `SpringApplication` external
10331130YAML configuration, as shown in the following example:
10341131
@@ -1097,8 +1194,8 @@ its bean registration, as shown in the following example:
10971194 }
10981195----
10991196
1100- Any property defined with the `another` prefix is mapped onto that `AnotherComponent` bean
1101- in manner similar to the preceding `AcmeProperties` example.
1197+ Any JavaBean property defined with the `another` prefix is mapped onto that
1198+ `AnotherComponent` bean in manner similar to the preceding `AcmeProperties` example.
11021199
11031200
11041201
0 commit comments