Any bean that is @ConfigurationPropertiesBinding will be created during bean post-processing for configuration property binding. As such, it may not be eligible for full post-processing which will result in a warning being logged. The warning will be something like this:

2025-05-20T15:19:49.363+01:00  WARN 26216 --- [gh-45618] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'stringOrNumberMigrationVersionConverter' of type [org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration$StringOrNumberToMigrationVersionConverter] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [methodValidationPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.

As the warning suggests, it can be avoided by declaring the bean in the infrastructure role (as well as declaring the bean method static (#45621)). I think we should meta-annotate @ConfigurationPropertiesBinding with @Role(BeanDefinition.ROLE_INFRASTRUCTURE) but I'd like to discuss this with the team to make sure I haven't overlooked anything and to agree upon the branches that will have the fix applied.

Comment From: wilkinsona

We discussed this today and decided that the recommendation to use static is sufficient.

Comment From: nosan

I am not sure that the static modifier alone is sufficient, at least the following test shows otherwise:

@ExtendWith(OutputCaptureExtension.class)
class ConfigurationPropertiesBindingTests {

    @Test
    void infracheck(CapturedOutput capturedOutput) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(InfraBeanPostProcessorConfiguration.class, InfraConfig.class);
        context.refresh();
        context.close();
        assertThat(capturedOutput.getAll()).doesNotContain("is not eligible for getting processed");
    }

    @ConfigurationProperties("infra")
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    static class InfraProperties {

    }

    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties(InfraProperties.class)
    static class InfraConfig {

        @Bean
        @ConfigurationPropertiesBinding
        static Converter<Object, Object> infraConverter() {
            return (source) -> source;
        }

    }

    @Configuration(proxyBeanMethods = false)
    static class InfraBeanPostProcessorConfiguration {

        @Bean
        static InfraBeanPostProcessor infraBeanPostProcessor(InfraProperties infraProperties) {
            return new InfraBeanPostProcessor();
        }

    }

    static class InfraBeanPostProcessor implements BeanPostProcessor {

    }

}

When @Role(BeanDefinition.ROLE_INFRASTRUCTURE) is used together with @ConfigurationPropertiesBinding on the converter's @Bean method, the warning is no longer logged.

Comment From: wilkinsona

Yes, that's right. However, our opinion is that the better fix is not to inject dependencies into a method that defines a BeanPostProcessor. The static just prevents InfraBeanPostProcessorConfiguration from also being affected.

Comment From: nosan

I understand this, but since @Role(BeanDefinition.ROLE_INFRASTRUCTURE) can be used on @ConfigurationProperties, users might expect that they can inject it into a BeanPostProcessor (which is a valid use case). However, they cannot, due to @ConfigurationPropertiesBinding. I think this is exactly what happened in https://github.com/spring-projects/spring-boot/issues/45618. Everyone expected it to work, but it did not.

If, for some reason, someone tries to do the same in the future, they will probably face the same issue because of a similar cause, and users can't change FlywayAutoConfiguration or something else in the Spring Boot codebase to avoid this issue.