Spring Boot 3. See minimized example project: https://github.com/matthenry87/configprops-bug

    @Configuration
    @ConfigurationProperties(prefix = "foo")
    static class ConfigPropertiesConfigBean {

        private final Environment environment;

        private String bar;

        public ConfigPropertiesConfigBean(Environment environment) {

            this.environment = environment;
        }

        public String getBar() {

            return bar;
        }

        public void setBar(String bar) {

            this.bar = bar;
        }

    }
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'configpropsBugApplication.ConfigPropertiesConfigBean' defined in file [C:\workspace\configprops-bug\target\classes\org\matthenry87\configpropsbug\ConfigpropsBugApplication$ConfigPropertiesConfigBean.class]: Cannot bind @ConfigurationProperties for bean 'configpropsBugApplication.ConfigPropertiesConfigBean'. Ensure that @ConstructorBinding has not been applied to regular bean
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:931) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:916) ~[spring-context-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-context-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.matthenry87.configpropsbug.ConfigpropsBugApplication.main(ConfigpropsBugApplication.java:14) ~[classes/:na]
Caused by: java.lang.IllegalStateException: Cannot bind @ConfigurationProperties for bean 'configpropsBugApplication.ConfigPropertiesConfigBean'. Ensure that @ConstructorBinding has not been applied to regular bean
    at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:86) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:78) ~[spring-boot-3.0.0-RC1.jar:3.0.0-RC1]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:420) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1745) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.0.0-RC2.jar:6.0.0-RC2]
    ... 14 common frames omitted

Comment From: wilkinsona

Thanks for trying the release candidate. As described in the release notes, you should use @Autowired when you want a constructor to be used for dependency injection rather than property binding.

Unrelated to your problem, a class shouldn't be both @Configuration and @ConfigurationProperties. The former is used to indicate that a class will define beans via @Bean methods.

Comment From: matthenry87

@wilkinsona A couple things though:

  • Using @Autowired on the constructor doesn't make the error go away.
  • In Spring Boot 2.x having a class be both @Configuration and @ConfigurationProperties works just fine.

Comment From: matthenry87

This is a common use case for my teams, where we want to pull in properties in order to pass them to the beans we're creating in the Configuration class, without having to have a separate class that we have to inject for access to the props.

Comment From: wilkinsona

Using @Autowired on the constructor doesn't make the error go away

Interesting. I believe it should have worked. I'll re-open the issue so that we can investigate.

In Spring Boot 2.x having a class be both @Configuration and @ConfigurationProperties works just fine

As I said above, it's not related to the problem you're seeing, but the recommendations stands: a class should not be annotated with both @Configuration and @ConfigurationProperties. Note that this isn't a "must not" so it is just a recommendation.

Comment From: matthenry87

I've updated the above demo project, adding @Autowired to the constructor.

Do you have additional information, or can you elaborate on that recommendation? Using @Configuration in combination with @ConfigurationProperties is something my teams do almost exclusively. If a bean doesn't need properties then we just annotate the class with a stereotype annotation directly.

If a bean needs properties to be configured then we want to pass the properties it needs while we are configuring the bean inside of the @Bean method, without having to have a dedicated class just to hold the properties that we would also have to inject into the @Configuration class. Is the recommendation just for separation of concerns? Or are there other negative consequences?

In your experience is it more common that the @ConfigurationProperties class/bean is injected into the bean that needs it rather than passing the fields to it inside of the @Bean method?

Comment From: wilkinsona

The problem's caused by the CGLib proxy that the use of @Configuration creates . With @Autowired in place, it works when @Configuration(proxyBeanMethods = false) is used. As expected, @Component works as well.

Comment From: wilkinsona

Is the recommendation just for separation of concerns? Or are there other negative consequences?

It's largely for separation of concerns. You also need to consider that @Configuration is a specialisation of @Component so your configuration property beans will now be picked up by component scanning. That may not be what you want.

In your experience is it more common that the @ConfigurationProperties class/bean is injected into the bean that needs it rather than passing the fields to it inside of the @Bean method?

Yes. In Boot's own codebase, our @ConfigurationProperties classes are separate from our @Configuration classes with the former being injected into the latter as needed. The documentation also does not mix @Configuration and @ConfigurationProperties on the same class. From what I've seen, most people have followed this pattern in their own code.

Comment From: wilkinsona

I've got a possible fix in this branch. I'd like someone else on the team to take a look as I'm not entirely sure it's the right approach. I think it'll work fine when there's no bind constructor. I'm not sure if things will go wrong if a bind constructor's found and used as the instance that's created won't be from the proxy class.

Comment From: philwebb

@wilkinsona I've refined it a bit and pushed. I think we should consider all constructors when looking for @Autowired, including those on user-classes if it's a proxy.