Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot 1.5 validated ConfigurationProperties

Tags:

spring-boot

Jump to the bottom for the motivations and the solutions to this issue!

In the process of upgrading from Spring Boot 1.4 to 1.5 I read (source: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.5-Release-Notes#upgrading-from-spring-boot-14)

If you have @ConfigurationProperties classes that use JSR-303 constraint annotations, you should now additionally annotate them with @Validated. Existing validation will currently continue to work, however, a warning will be logged. In the future, classes without @Validated will not be validated at all.

So, diligently, I add @Validated to all of mine configuration properties. Now I have a specific use case that breaks, aka the property is not loaded anymore (I summarize first, then add code).

If I use a template property defined in application.properties file and then try to override the value for specific profiles, then the application is not starting.

Here is some sample code to reproduce (relevant files):

build.gradle

buildscript {
    ext {
        springBootVersion = '1.5.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

application.properties : demo.prop=${profile.prop}

application-demo.properties : profile.prop=demo

DemoApplication.java

package package;

import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @org.springframework.web.bind.annotation.RestController
    public static class RestController {

        @Autowired
        private DemoProperties properties;

        @GetMapping
        public String get() {
            return properties.prop == null ? "null" : properties.prop;
        }
    }

    @Component
    @ConfigurationProperties(prefix = "demo")
//  @Validated
    public static class DemoProperties {

        @NotNull
        private String prop;

        public void setProp(String prop) {
            this.prop = prop;
        }

        public String getProp() {
            return prop;
        }
    }
}

As it stands, my application produces the expected result when run with -Dspring.profiles.active=demo

curl "http://localhost:8080"
demo

however, uncommenting //@validated and running the application as before produces

curl "http://localhost:8080"
null

Full application available at https://github.com/ThanksForAllTheFish/boot-props (including a test case showing that defining profile.prop in config/application.properties fails as well with @validated but succeeds without).

I guess it is a bug in Spring Boot, but it may me not understanding something, so SoF first (as hinted in Spring Boot issues manager on github).

This github issue seems related: https://github.com/spring-projects/spring-boot/issues/8173

like image 296
ThanksForAllTheFish Avatar asked Feb 04 '26 16:02

ThanksForAllTheFish


1 Answers

As I found the solution to my issue (some time ago already, but added as explanation in the question itself), I figured it may be more helpful to copy my findings here.

The problem with my sample code is that @Validated wraps the real class with a proxy, so that validation concerns can be injected, therefore return properties.prop == null ? "null" : properties.prop; is actually trying to access the prop field of the proxy. Changing to getProp() is the fix. Pretty obvious once found out.

Regarding production code: the issue was related to https://github.com/spring-projects/spring-boot/issues/8173, or more precisely to https://github.com/spring-cloud/spring-cloud-commons/issues/177, as we use spring-cloud. Basically, there was a conflict in BeanPostProcessor between spring-cloud and spring-boot (details in the ticket on github) that was solved in Dalston.RELEASE of spring-cloud. Just updating the dependency in our project solved the issue in production as well. Lot of digging and testing to just change 7 characters in our codebase.

like image 69
ThanksForAllTheFish Avatar answered Feb 13 '26 07:02

ThanksForAllTheFish



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!