Closed alexey-anufriev closed 2 weeks ago
Thanks for the report.
This appears to be a more general problem and is not specific to TaskExecutorMetricsAutoConfiguration
. For example, if you define a DataSource
with autowireCandidate = false
, JdbcTemplateAutoConfiguration
will fail like this:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jdbcTemplate' defined in org.springframework.boot.autoconfigure.jdbc.JdbcTemplateConfiguration: Unsatisfied dependency expressed through method 'jdbcTemplate' parameter 0: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1351)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1181)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:296)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1115)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1086)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1025)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.configureContext(AbstractApplicationContextRunner.java:428)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.createAndLoadContext(AbstractApplicationContextRunner.java:403)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.lambda$4(AbstractApplicationContextRunner.java:389)
at org.springframework.boot.test.context.assertj.AssertProviderApplicationContextInvocationHandler.getContextOrStartupFailure(AssertProviderApplicationContextInvocationHandler.java:61)
at org.springframework.boot.test.context.assertj.AssertProviderApplicationContextInvocationHandler.<init>(AssertProviderApplicationContextInvocationHandler.java:48)
at org.springframework.boot.test.context.assertj.ApplicationContextAssertProvider.get(ApplicationContextAssertProvider.java:113)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.createAssertableContext(AbstractApplicationContextRunner.java:389)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.consumeAssertableContext(AbstractApplicationContextRunner.java:362)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.lambda$1(AbstractApplicationContextRunner.java:341)
at org.springframework.boot.test.util.TestPropertyValues.lambda$5(TestPropertyValues.java:174)
at org.springframework.boot.test.util.TestPropertyValues.applyToSystemProperties(TestPropertyValues.java:188)
at org.springframework.boot.test.util.TestPropertyValues.applyToSystemProperties(TestPropertyValues.java:173)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.lambda$0(AbstractApplicationContextRunner.java:341)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.withContextClassLoader(AbstractApplicationContextRunner.java:369)
at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.run(AbstractApplicationContextRunner.java:340)
at org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfigurationTests.testWithNonAutowireableDataSource(JdbcTemplateAutoConfigurationTests.java:211)
There's a mismatch between @ConditionalOnBean
finding a matching bean in the bean factory and autowiring then being unable to use that bean as it's not an autowiring candidate.
At this point, Spring Boot's auto-configuration isn't compatible with autowireCandidate = false
and I don't think there's a workaround. For now, if you want to use auto-configuration, you'll have to avoid defining beans that are not autowire candidates if those beans are the subject of @ConditionalOnBean
.
@alexey-anufriev what's your use case for an autowireCandidate = false
bean in a Spring Boot app? If you're trying to hide something from other consumers, a common approach is to wrap it in a "container" type such as DefaultSockJsSchedulerContainer
in Spring Framework.
@alexey-anufriev what's your use case for an
autowireCandidate = false
bean in a Spring Boot app? If you're trying to hide something from other consumers, a common approach is to wrap it in a "container" type such asDefaultSockJsSchedulerContainer
in Spring Framework.
My case is very close to my initially reported code, I have a ThreadPoolTaskExecutor
dedicated to a concrete purpose. I could have wrapped it and hide from possible injections in other places in the code but ThreadPoolTaskExecutor
itself has a pretty complex lifecycle and I would like Spring to manage it for me. Duplicating all the interfaces and delegating to the wrapped instance would lead to quite a lot of boilerplate code.
We discussed this today and we consider this a bug, but one that is quite risky to fix before 3.4. We're considering adding some additional attribute to the ...OnBean
condition annotations to allow autowireCandidate
on the bean definition to be considered.
Do you have in mind how will this work? Will the new default behavior be a breaking change? I mean, for now, non-candidates are skipped by default. Will I be required to consider those, or will I be required to exclude those?
We're not yet certain and we'd like to do some experimentation. Right now, I think it's likely that it will be a breaking change and we'll start ignoring non-autowire candidates by default when looking for matching beans. We'll have to wait and see though.
It should be backported since Bean#autowireCandidate()
is not new feature of Spring Framework, WDYT @wilkinsona
We think it's a bit risky for a backport since it's a change in behavior.
We think it's a bit risky for a backport since it's a change in behavior.
It's a bug fix, I can't imagine which user case would rely the buggy behavior.
It's also the risk of regression due to unexpected or accidental side-effects. Given that the problem has existed with XML config for Boot's entire life and with Java config since Boot 2.1 yet it was only reported for the first time last month, I see no need to back port the fix and risk destabilising maintenance branches.
Assuming the following test:
And having
spring-boot-starter-actuator
andmicrometer-core
in dependencies the test fails with:Reproducer: https://github.com/alexey-anufriev/autowire-candidate-error