Affects: >= 5.2

The @Configuration(proxyBeanMethods=false) option has become the default for @AutoConfiguration and the recommended option for regular Configurations due to the performance benefits (https://github.com/spring-projects/spring-boot/issues/9068).

There is one disadvantage though - with proxyBeanMethods=false, there is no way to opt-out of autowiring (@Bean(autowireCandidate = false)) while still allowing to inject the bean to other beans defined in the same configuration.

To illustrate what is currently not possible (fails with No qualifying bean of type 'org.springframework.core.task.TaskExecutor' available):

@Configuration(proxyBeanMethods=false)
class MyConfiguration {
    private static final String RABBIT_LISTENER_TASK_EXECUTOR_BEAN_ID = "rabbitListenerShipMonkTaskExecutor";

    @Bean
    public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
        final SimpleRabbitListenerContainerFactoryConfigurer configurer,
        final ConnectionFactory connectionFactory,
        @Qualifier(RABBIT_LISTENER_TASK_EXECUTOR_BEAN_ID) final ShipMonkAsyncTaskExecutor rabbitListenerTaskExecutor
    ) {
    }


    @Bean(name = RABBIT_LISTENER_TASK_EXECUTOR_BEAN_ID, autowireCandidate = false)
    public TaskExecutor rabbitListenerTaskExecutor() {
    }
}

The code above can be fixed by removing the autowireCandidate option.

The issue with autowireCandidate=true though is that the registration of the bean may break the application as it becomes automatically "wirable" by type globally. With proxyBeanMethods=true, I could achieve it safely in following way:

@Configuration(proxyBeanMethods=false)
class MyConfiguration {
    private static final String RABBIT_LISTENER_TASK_EXECUTOR_BEAN_ID = "rabbitListenerShipMonkTaskExecutor";

    @Bean
    public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
        final SimpleRabbitListenerContainerFactoryConfigurer configurer,
        final ConnectionFactory connectionFactory
    ) {
        var executor = rabbitListenerTaskExecutor();
    }


    @Bean(autowireCandidate = false)
    public TaskExecutor rabbitListenerTaskExecutor() {
    }
}

Summary Sometimes, it's beneficial to be able to autowire concrete beans (e.g. by qualifier) without exposing them for autowiring by type. Currently, with proper use of @Qualifier and @Primary annotations, the problem can be resolved but the ability to wire beans by qualifier only and not by type would be nice addition in my opinion.

Additional question 1. I understand that this is most likely not an issue of default Spring @AutoConfiguration. Does the recommendation to use proxyBeanMethods=false apply mainly to those situations (as they register the default beans which should be available and autowirable) and custom application configurations can freely utilize proxyBeanMethods=true? What is the recommended approach of Spring Framework for application developers? 2. Is there something I'm missing and it's solvable with proxyBeanMethods=false even now? 3. Is there any relation between this option (proxyBeanMethods) and Spring Native support?

Thanks!

Comment From: snicoll

I am assuming the second example was supposed to be @Configuration(proxyBeanMethods=true) ?

I am assuming TaskExecutor requires some lifecycle and that's why you expose it as a bean? At the end of the day, it is a matter of what you want to container to do for you vs. extra code and simplicity. You haven't really shared why you don't want the TaskScheduler to be exposed. If that's because the auto-configuration backs off, a builder is provided to let you reproduce it if necessary.

If you don't need a proxy, then you should really set the method to false. Spring Boot was able to enforce that with @AutoConfiguration as it is a new annotation but we can't do the same with @Configuration.

I don't think you're missing anything. You can't obviously inject something for which you've disabled autowiring. You could keep the instance private to the configuration class and have your configuration class implements the necessary callback that would delegate to your internal TaskScheduler (in particular DisposableBean). If you don't want of that boilerplate, then creating a proxy still works.

There is no relation between this and Native support, except that the original project didn't really support CGLIB proxy initially. It's fine now we hava GAed in SF6 and SB3.