Close

Spring Boot - @ConfigurationProperties Cascaded Validation by using @Valid

[Updated: Aug 14, 2017, Created: Aug 14, 2017]

In the last example, we saw how to use JSR 303/349 validation annotations with @ConfigurationProperties. In following example, we will see how to validate nested (cascaded) configuration properties by using @Valid annotation.

Example

The external properties

src/main/resources/application.properties

spring.main.banner-mode=off 
spring.main.logStartupInfo=false
app.main-screen-properties.refresh-rate=15
app.main-screen-properties.width=10
app.main-screen-properties.height=400
app.admin-contact-number.value=111-111-111
app.admin-contact-number.type=personal

@ConfigurationProperties class

@Component
@ConfigurationProperties("app")
@Validated
public class MyAppProperties {
  @NotNull
  @Valid
  private MainScreenProperties mainScreenProperties;

  @NotNull
  @Valid
  private PhoneNumber adminContactNumber;
    .............
}
public class PhoneNumber {
  @Pattern(regexp = "\\d{3}-\\d{3}-\\d{4}")
  private String value;
  @Pattern(regexp = "(?i)cell|house|work")
  private String type;
    .............
}
public class MainScreenProperties {
  @Min(1)
  private int refreshRate;
  @Min(50)
  @Max(1000)
  private int width;
  @Min(50)
  @Max(600)
  private int height;
    .............
}

We purposely added such values in the external properties file, which should fail per above validation annotations to see whether or not the cascaded validation is performed as expected.

The Main class

@SpringBootApplication
public class ExampleMain {

  public static void main(String[] args) throws InterruptedException {
      ConfigurableApplicationContext context = SpringApplication.run(ExampleMain.class, args);
      MyAppProperties bean = context.getBean(MyAppProperties.class);
      System.out.println(bean);
  }
}

Output

2017-08-14 16:24:30.839  INFO 3464 --- [mpleMain.main()] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5a5bd185: startup date [Mon Aug 14 16:24:30 CDT 2017]; root of context hierarchy
2017-08-14 16:24:31.103 WARN 3464 --- [mpleMain.main()] o.h.v.m.ParameterMessageInterpolator : HV000184: ParameterMessageInterpolator has been chosen, EL interpolation will not be supported
2017-08-14 16:24:31.170 WARN 3464 --- [mpleMain.main()] o.h.v.m.ParameterMessageInterpolator : HV000184: ParameterMessageInterpolator has been chosen, EL interpolation will not be supported
2017-08-14 16:24:31.265 ERROR 3464 --- [mpleMain.main()] o.s.b.b.PropertiesConfigurationFactory : Properties configuration failed validation
2017-08-14 16:24:31.265 ERROR 3464 --- [mpleMain.main()] o.s.b.b.PropertiesConfigurationFactory : Field error in object 'app' on field 'adminContactNumber.type': rejected value [personal]; codes [Pattern.app.adminContactNumber.type,Pattern.adminContactNumber.type,Pattern.type,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.type,adminContactNumber.type]; arguments []; default message [adminContactNumber.type],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@40cd62f1]; default message [must match "(?i)cell|house|work"]
2017-08-14 16:24:31.265 ERROR 3464 --- [mpleMain.main()] o.s.b.b.PropertiesConfigurationFactory : Field error in object 'app' on field 'adminContactNumber.value': rejected value [111-111-111]; codes [Pattern.app.adminContactNumber.value,Pattern.adminContactNumber.value,Pattern.value,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.value,adminContactNumber.value]; arguments []; default message [adminContactNumber.value],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@6ff1a3a2]; default message [must match "\d{3}-\d{3}-\d{4}"]
2017-08-14 16:24:31.265 ERROR 3464 --- [mpleMain.main()] o.s.b.b.PropertiesConfigurationFactory : Field error in object 'app' on field 'mainScreenProperties.width': rejected value [10]; codes [Min.app.mainScreenProperties.width,Min.mainScreenProperties.width,Min.width,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.mainScreenProperties.width,mainScreenProperties.width]; arguments []; default message [mainScreenProperties.width],50]; default message [must be greater than or equal to 50]
2017-08-14 16:24:31.266 WARN 3464 --- [mpleMain.main()] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myAppProperties': Could not bind properties to MyAppProperties (prefix=app, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 3 errors
Field error in object 'app' on field 'adminContactNumber.type': rejected value [personal]; codes [Pattern.app.adminContactNumber.type,Pattern.adminContactNumber.type,Pattern.type,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.type,adminContactNumber.type]; arguments []; default message [adminContactNumber.type],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@40cd62f1]; default message [must match "(?i)cell|house|work"]
Field error in object 'app' on field 'adminContactNumber.value': rejected value [111-111-111]; codes [Pattern.app.adminContactNumber.value,Pattern.adminContactNumber.value,Pattern.value,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.value,adminContactNumber.value]; arguments []; default message [adminContactNumber.value],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@6ff1a3a2]; default message [must match "\d{3}-\d{3}-\d{4}"]
Field error in object 'app' on field 'mainScreenProperties.width': rejected value [10]; codes [Min.app.mainScreenProperties.width,Min.mainScreenProperties.width,Min.width,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.mainScreenProperties.width,mainScreenProperties.width]; arguments []; default message [mainScreenProperties.width],50]; default message [must be greater than or equal to 50]
2017-08-14 16:24:31.270 INFO 3464 --- [mpleMain.main()] utoConfigurationReportLoggingInitializer :

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-08-14 16:24:31.272 ERROR 3464 --- [mpleMain.main()] o.s.b.d.LoggingFailureAnalysisReporter :

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target MyAppProperties{
mainScreenProperties=MainScreenProperties{refreshRate=15, width=10, height=400}, adminContactNumber=PhoneNumber{value='111-111-111', type='personal'}
} failed:

Property: app.adminContactNumber.type
Value: personal
Reason: must match "(?i)cell|house|work"

Property: app.adminContactNumber.value
Value: 111-111-111
Reason: must match "\d{3}-\d{3}-\d{4}"

Property: app.mainScreenProperties.width
Value: 10
Reason: must be greater than or equal to 50


Action:

Update your application's configuration

[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:294)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myAppProperties': Could not bind properties to MyAppProperties (prefix=app, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 3 errors
Field error in object 'app' on field 'adminContactNumber.type': rejected value [personal]; codes [Pattern.app.adminContactNumber.type,Pattern.adminContactNumber.type,Pattern.type,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.type,adminContactNumber.type]; arguments []; default message [adminContactNumber.type],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@40cd62f1]; default message [must match "(?i)cell|house|work"]
Field error in object 'app' on field 'adminContactNumber.value': rejected value [111-111-111]; codes [Pattern.app.adminContactNumber.value,Pattern.adminContactNumber.value,Pattern.value,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.value,adminContactNumber.value]; arguments []; default message [adminContactNumber.value],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@6ff1a3a2]; default message [must match "\d{3}-\d{3}-\d{4}"]
Field error in object 'app' on field 'mainScreenProperties.width': rejected value [10]; codes [Min.app.mainScreenProperties.width,Min.mainScreenProperties.width,Min.width,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.mainScreenProperties.width,mainScreenProperties.width]; arguments []; default message [mainScreenProperties.width],50]; default message [must be greater than or equal to 50]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:334)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:291)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:409)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1620)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
at com.logicbig.example.ExampleMain.main(ExampleMain.java:11)
... 6 more
Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 3 errors
Field error in object 'app' on field 'adminContactNumber.type': rejected value [personal]; codes [Pattern.app.adminContactNumber.type,Pattern.adminContactNumber.type,Pattern.type,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.type,adminContactNumber.type]; arguments []; default message [adminContactNumber.type],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@40cd62f1]; default message [must match "(?i)cell|house|work"]
Field error in object 'app' on field 'adminContactNumber.value': rejected value [111-111-111]; codes [Pattern.app.adminContactNumber.value,Pattern.adminContactNumber.value,Pattern.value,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.adminContactNumber.value,adminContactNumber.value]; arguments []; default message [adminContactNumber.value],[Ljavax.validation.constraints.Pattern$Flag;@3afee74c,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@6ff1a3a2]; default message [must match "\d{3}-\d{3}-\d{4}"]
Field error in object 'app' on field 'mainScreenProperties.width': rejected value [10]; codes [Min.app.mainScreenProperties.width,Min.mainScreenProperties.width,Min.width,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [app.mainScreenProperties.width,mainScreenProperties.width]; arguments []; default message [mainScreenProperties.width],50]; default message [must be greater than or equal to 50]
at org.springframework.boot.bind.PropertiesConfigurationFactory.checkForBindingErrors(PropertiesConfigurationFactory.java:359)
at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:276)
at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:240)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:329)
... 24 more

Example Project

Dependencies and Technologies Used:

  • spring-boot-starter 1.5.6.RELEASE: Core starter, including auto-configuration support, logging and YAML.
    Corresponding Spring version: 4.3.10.RELEASE
  • hibernate-validator 5.3.5.Final: Hibernate's Bean Validation (JSR-303) reference implementation.
  • JDK 1.8
  • Maven 3.3.9

@ConfigurationProperties Cascaded Validation Example Select All Download
  • configuration-properties-nested-validation
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources

See Also