Centinul
Centinul

Reputation: 884

Spring Boot @ConfigurationProperties not retrieving properties from Environment

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

Answers (2)

karibe50
karibe50

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

Dave Syer
Dave Syer

Reputation: 58094

There's no setter in your bean. Add a setter.

Upvotes: 30

Related Questions