Allow @ConstructorBinding to be optional

This commit makes @ConstructorBinding optional for a type
that has a single parameterized constructor. An @Autowired annotation
on any of the constructors indicates that the type should not be constructor
bound.

Since @ConstructorBinding is now deduced for a single parameterized constructor,
the annotation is no longer needed at the type level.

Closes gh-23216
This commit is contained in:
Madhura Bhave
2021-12-10 09:45:48 -08:00
parent bc2c637d63
commit 44b88cc88c
44 changed files with 693 additions and 391 deletions

View File

@@ -63,7 +63,8 @@ You could also let the AspectJ plugin run all the processing and disable annotat
=== Automatic Metadata Generation
The processor picks up both classes and methods that are annotated with `@ConfigurationProperties`.
If the class is also annotated with `@ConstructorBinding`, a single constructor is expected and one property is created per constructor parameter.
If the class has a single parameterized constructor, one property is created per constructor parameter, unless the constructor is annotated with `@Autowired`.
If the class has a constructor explicitly annotated with `@ConstructorBinding`, one property is created per constructor parameter for that constructor.
Otherwise, properties are discovered through the presence of standard getters and setters with special handling for collection and map types (that is detected even if only a getter is present).
The annotation processor also supports the use of the `@Data`, `@Value`, `@Getter`, and `@Setter` lombok annotations.

View File

@@ -704,12 +704,14 @@ The example in the previous section can be rewritten in an immutable fashion as
include::code:MyProperties[]
In this setup, the `@ConstructorBinding` annotation is used to indicate that constructor binding should be used.
This means that the binder will expect to find a constructor with the parameters that you wish to have bound.
In this setup, the presence of a single parameterized constructor implies that constructor binding should be used.
This means that the binder will find a constructor with the parameters that you wish to have bound.
If your class has multiple constructors, the `@ConstructorBinding` annotation can be used to specify which constructor to use for constructor binding.
To opt out of constructor binding for a class with a single parameterized constructor, the constructor must be annotated with `@Autowired`.
If you are using Java 16 or later, constructor binding can be used with records.
In this case, unless your record has multiple constructors, there is no need to use `@ConstructorBinding`.
Unless your record has multiple constructors, there is no need to use `@ConstructorBinding`.
Nested members of a `@ConstructorBinding` class (such as `Security` in the example above) will also be bound through their constructor.
Nested members of a constructor bound class (such as `Security` in the example above) will also be bound through their constructor.
Default values can be specified using `@DefaultValue` and the same conversion service will be applied to coerce the `String` value to the target type of a missing property.
By default, if no properties are bound to `Security`, the `MyProperties` instance will contain a `null` value for `security`.
@@ -721,8 +723,6 @@ include::code:nonnull/MyProperties[tag=*]
NOTE: To use constructor binding the class must be enabled using `@EnableConfigurationProperties` or configuration property scanning.
You cannot use constructor binding with beans that are created by the regular Spring mechanisms (for example `@Component` beans, beans created by using `@Bean` methods or beans loaded by using `@Import`)
TIP: If you have more than one constructor for your class you can also use `@ConstructorBinding` directly on the constructor that should be bound.
NOTE: The use of `java.util.Optional` with `@ConfigurationProperties` is not recommended as it is primarily intended for use as a return type.
As such, it is not well-suited to configuration property injection.
For consistency with properties of other types, if you do declare an `Optional` property and it has no value, `null` rather than an empty `Optional` will be bound.

View File

@@ -108,11 +108,10 @@ TIP: `org.jetbrains.kotlinx:kotlinx-coroutines-reactor` dependency is provided b
[[features.kotlin.configuration-properties]]
=== @ConfigurationProperties
`@ConfigurationProperties` when used in combination with <<features#features.external-config.typesafe-configuration-properties.constructor-binding,`@ConstructorBinding`>> supports classes with immutable `val` properties as shown in the following example:
`@ConfigurationProperties` when used in combination with <<features#features.external-config.typesafe-configuration-properties.constructor-binding,constructor binding>> supports classes with immutable `val` properties as shown in the following example:
[source,kotlin,indent=0,subs="verbatim"]
----
@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,