Reputation: 884
I'm using Spring Boot 1.2.1 and trying to create a @ConfigurationProperties
bean with validation like so:
package com.sampleapp;
import java.net.URL;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties
public class SampleAppProperties {
@NotNull
private URL url;
public URL getUrl() {
return url;
}
}
The class to bootstrap the application is:
package com.sampleapp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
@SpringBootApplication
@EnableConfigurationProperties
public class SampleApplication implements EnvironmentAware {
private static Logger LOGGER = LoggerFactory.getLogger(SampleApplication.class);
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
@Override
public void setEnvironment(Environment environment) {
LOGGER.info("URL = {}", environment.getRequiredProperty("url"));
}
}
When I try and start this application I receive the following exception stack:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sampleAppProperties': Could not bind properties to [unknown] (target=, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'target' on field 'url': rejected value [null]; codes [NotNull.target.url,NotNull.url,NotNull.java.net.URL,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.url,url]; arguments []; default message [url]]; default message [may not be null]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:303)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:250)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1558)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:961)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:950)
at com.sampleapp.SampleApplication.main(SampleApplication.java:17)
Caused by: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'target' on field 'url': rejected value [null]; codes [NotNull.target.url,NotNull.url,NotNull.java.net.URL,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.url,url]; arguments []; default message [url]]; default message [may not be null]
at org.springframework.boot.bind.PropertiesConfigurationFactory.validate(PropertiesConfigurationFactory.java:294)
at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:253)
at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:225)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:296)
... 17 common frames omitted
As you can see in the setEnvironment
method above I'm logging the url
property to validate that it's part of the Environment
and it is displayed before the exception:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.2.1.RELEASE)
2015-02-12 12:32:01.384 INFO 5608 --- [ main] c.s.SampleApplication : Starting SampleApplication on VDDK03E-14FB6E5 with PID 5608 (D:\projects\onboarding-parser\target\classes started by .....
2015-02-12 12:32:01.509 INFO 5608 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3252ac20: startup date [Thu Feb 12 12:32:01 EST 2015]; root of context hierarchy
2015-02-12 12:32:03.040 INFO 5608 --- [ main] c.s.SampleApplication : URL = http://www.joe.com
2015-02-12 12:32:03.378 ERROR 5608 --- [ main] o.s.b.b.PropertiesConfigurationFactory : Properties configuration failed validation
2015-02-12 12:32:03.378 ERROR 5608 --- [ main] o.s.b.b.PropertiesConfigurationFactory : Field error in object 'target' on field 'url': rejected value [null]; codes [NotNull.target.url,NotNull.url,NotNull.java.net.URL,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.url,url]; arguments []; default message [url]]; default message [may not be null]
2015-02-12 12:32:03.394 WARN 5608 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt
The url
property is pulled from the application.properties
file in src/main/resources
. The contents of the file are:
url=http://www.joe.com
Upvotes: 14
Views: 28778
Reputation: 71
It's clearly written here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
Getters and setters are usually mandatory, since binding is via standard Java Beans property descriptors, just like in Spring MVC. There are cases where a setter may be omitted: Maps, as long as they are initialized, need a getter but not necessarily a setter since they can be mutated by the binder. Collections and arrays can be either accessed via an index (typically with YAML) or using a single comma-separated value (properties). In the latter case, a setter is mandatory. We recommend to always add a setter for such types. If you initialize a collection, make sure it is not immutable (as in the example above) If nested POJO properties are initialized (like the Security field in the example above), a setter is not required. If you want the binder to create the instance on-the-fly using its default constructor, you will need a setter. Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok doesn’t generate any particular constructor for such type as it will be used automatically by the container to instantiate the object.
Upvotes: 7