javaspringspring-boot

SpringBoot 3.3.6 @ConfigurationProperties injecting a null object into constructor


I'm upgrading a spring boot project from 2.7.x to 3.3.6.

Hitting a problem with a test using ApplicationContextRunner where the bean being injected into the constructor of a @ConfigurationProperties instance is null. The object injected into a @Bean method is not null.

The test below passes on 2.7.x but fails on 3.3.6. There appears to be a change with how the @ConfigurationProperties are being loaded in 3.x. When loaded as part of the full application (instead of the test ApplicationContextRunner/TrivialConfiguration) the non-null KafkaProperties is injected into the wrapper.

Can anyone suggest what changes I need to make? I can't see anything directly related in the migration guides.

@Getter
@ConfigurationProperties(prefix = "spring.kafka")
public class KafkaPropertiesWrapper {
    private final KafkaProperties kafkaProperties;

    public KafkaPropertiesWrapper(KafkaProperties kafkaProperties) {
        this.kafkaProperties = kafkaProperties;
    }
}
@Getter
public class AnotherWrapper {
    private final KafkaProperties kafkaProperties;

    public AnotherWrapper(KafkaProperties kafkaProperties) {
        this.kafkaProperties = kafkaProperties;
    }
}
class KafkaWrapperTest {
    @EnableConfigurationProperties({KafkaPropertiesWrapper.class, KafkaProperties.class})
    private static class TrivialConfiguration {
        @Bean
        AnotherWrapper anotherWrapper(KafkaProperties kafkaProperties) {
            return new AnotherWrapper(kafkaProperties);
        }
    }

    private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
            .withUserConfiguration(TrivialConfiguration.class);

    @Test
    void startsOk() {
        contextRunner
                .run(context -> {
                            assertThat(context).hasSingleBean(KafkaProperties.class);
                            assertThat(context).hasSingleBean(KafkaPropertiesWrapper.class);
                            KafkaPropertiesWrapper kafkaPropertiesWrapper = context.getBean(KafkaPropertiesWrapper.class);
                            assertThat(kafkaPropertiesWrapper.getKafkaProperties()).isNotNull();
                        }
                );
    }
}

Solution

  • As of Spring Boot 3.0, the constructor needs to be annotated with @Autowired to indicate that it should be used for dependency injection rather than property binding.