@ConfigurationProperties(prefix = "validateconfig")
and @Component
work with org.springframework.boot:spring-boot-autoconfigure:2.2.2.RELEASE
: the config bean's fields get populated.
However, if you add @Validated
and org.springframework.boot:spring-boot-starter-validation:2.2.2.RELEASE
, then the config bean is not populated anymore.
An example to reproduce:
git clone https://github.com/peter-kehl/validate-config-bug-report
cd validate-config-bug-report
./gradlew run
Output (the last two lines - conf value
and another conf value
show the problem):
:: Spring Boot :: (v2.2.2.RELEASE)
2020-01-11 22:34:01.812 INFO 7637 --- [ main] your.pack.App : Starting App on spectre with PID 7637 (/home/pkehl/validate-config-java/build/classes/java/main started by pkehl in /home/pkehl/validate-config-java)
2020-01-11 22:34:01.817 INFO 7637 --- [ main] your.pack.App : No active profile set, falling back to default profiles: default
2020-01-11 22:34:02.791 INFO 7637 --- [ main] your.pack.App : Started App in 1.545 seconds (JVM running for 2.014)
conf is of class class your.pack.Conf$$EnhancerBySpringCGLIB$$f1724790
conf value: null
another conf value: null
To see how the same config bean works without spring-boot-starter-validation
, edit build.gradle
, comment out the first compile
statement, uncomment the second compile
statement. Then ./gradlew clean run
. The output is similar to the above, except for:
conf is of class class your.pack.Conf
conf value: validateconfigvalue
another conf value: anothervalidateconfigvalue
Potentially related to https://github.com/spring-projects/spring-boot/issues/8173 or https://github.com/spring-cloud/spring-cloud-commons/issues/177.
(Also reported recently at https://stackoverflow.com/questions/55471653/spring-validated-annotation-prevents-configurationproproperties-value-injectio#comment97662035_55471653
If JDK version matters, I'm on OpenJDK 1.8.0_232-b09, 64-Bit Server VM (build 25.232-b09, mixed mode)).
Comment From: wilkinsona
Thanks very much for providing a sample. The problem that you're seeing is a side-effect of the use of CGLib proxies for validation.
The CGLib proxy is a sub-class of your Conf
class. This sub-classing shadows the value
and another
fields that you're accessing directly in your main method:
System.out.println( "conf value: " +conf.value );
System.out.println( "another conf value: " +conf.another );
This is retrieving the value
and another
fields on the proxy which are unaffected by the configuration property binding which sets the fields (via your setter methods) on the proxy's target.
You can fix your problem by using the getter methods instead:
System.out.println( "conf value: " +conf.getValue() );
System.out.println( "another conf value: " +conf.getAnother() );
You will see the expected values
conf value: validateconfigvalue
another conf value: anothervalidateconfigvalue
This change also has the added benefit of improving encapsulation as those fields can now be made private.
I've opened a Spring Framework issue to see if a change can be made to the documentation to note the side-effects of @Validated
and its proxying.