ConfigurationPropertiesRebinder calls a BeanPostProcessor itself outside of the lifecycle for which is is defined. ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization is not meant to be called directly, it is a BeanPostProcessor callback.
The side effect is that, while in a valid BPP, we'll get the raw bean since the proxies won't have been created yet. Spring Cloud is calling the same method with the actual bean that can be proxied.
Consider the following example:
@ConfigurationProperties("foo")
@Validated
public class SampleProperties {
@NotNull(message = "That should not be null")
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If you try to define such configuration with Spring Cloud, That code in ConfigurationPropertiesRebinder passes a proxy (because of @Validated). Hibernate Validator applies the validation on the proxy, the fields are all null:
Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 1 errors
Field error in object 'foo' on field 'name': rejected value [null]; codes [NotNull.foo.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [foo.name,name]; arguments []; default message [name]]; default message [That should not be null]
at org.springframework.boot.bind.PropertiesConfigurationFactory.checkForBindingErrors(PropertiesConfigurationFactory.java:359) ~[spring-boot-1.5.2.BUILD-SNAPSHOT.jar:1.5.2.BUILD-SNAPSHOT]
at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:276) ~[spring-boot-1.5.2.BUILD-SNAPSHOT.jar:1.5.2.BUILD-SNAPSHOT]
at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:240) ~[spring-boot-1.5.2.BUILD-SNAPSHOT.jar:1.5.2.BUILD-SNAPSHOT]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:329) ~[spring-boot-1.5.2.BUILD-SNAPSHOT.jar:1.5.2.BUILD-SNAPSHOT]
... 47 common frames omitted
If you move the constraint to the getter, that exact same use case will work.
There is a sample that reproduces this issue here
ConfigurationPropertiesRebindercalls aBeanPostProcessoritself outside of the lifecycle for which is is defined.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitializationis not meant to be called directly, it is aBeanPostProcessorcallback.The side effect is that, while in a valid
BPP, we'll get the raw bean since the proxies won't have been created yet. Spring Cloud is calling the same method with the actual bean that can be proxied.Consider the following example:
If you try to define such configuration with Spring Cloud, That code in
ConfigurationPropertiesRebinderpasses a proxy (because of@Validated). Hibernate Validator applies the validation on the proxy, the fields are all null:If you move the constraint to the getter, that exact same use case will work.
There is a sample that reproduces this issue here