Skip to content

Rebinder calls Spring Boot's BPP in a totally different lifecyle #177

@snicoll

Description

@snicoll

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

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions