Reputation: 87
I have a bean of type Provider<Foo>
in my Spring context. I'd like to @Autowire
that bean into another class.
@Autowired
private Provider<Foo> fooProvider;
However, Spring looks at this and decides that I must've defined a bean of type Foo
in my context, and that I want that wrapped that in a Provider
so that Spring can manage its scope.
That's not what I want; I don't have a Foo
, I have a Provider<Foo>
. Spring complains:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [my.package.Foo] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
How can I ask Spring to be less clever and just autowire the type I want?
Thanks for the questions. I created a test case to illustrate my problem:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:foo-context.xml")
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class
})
public class FooProviderTest {
@Autowired
private javax.inject.Provider<Foo> fooProvider;
@Test
public void verifyFooProviderAutowired() {
assertEquals("foo", fooProvider.get().getFooName());
}
}
class Foo {
public String getFooName() {
return "foo";
}
}
class SimpleFooProvider implements javax.inject.Provider<Foo> {
@Override
public Foo get() {
return new Foo();
}
}
The foo-context.xml file is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<context:annotation-config/>
<bean id="fooProvider" class="example.SimpleFooProvider"/>
</beans>
The output of the test:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [example.Foo] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:947)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:816)
at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectFactory.getObject(DefaultListableBeanFactory.java:1035)
at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyProvider.get(DefaultListableBeanFactory.java:1051)
at example.FooProviderTest.verifyFooProviderAutowired(FooProviderTest.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Note that the test does not fail on the assertion; it fails to initialise the application context.
Curiously, explicitly changing the test field's declared type from Provider<Foo>
to SimpleFooProvider
results in the test passing with no complaint. That is,
@Autowired
private SimpleFooProvider fooProvider;
Upvotes: 3
Views: 2819
Reputation: 280054
Assuming Provider
is javax.inject.Provider
, Spring will very much autowire a Provider
implementation into this field
@Autowired
private Provider<Foo> fooProvider;
For example, this is done through the following call hierarchy
AutowiredAnnotationBeanPostProcessor#inject(Object bean, String beanName, PropertyValues pvs)
DefaultListableBeanFactory#resolveDependency(DependencyDescriptor descriptor, String beanName,Set<String> autowiredBeanNames, TypeConverter typeConverter)
DependencyProviderFactory#createDependencyProvider(DependencyDescriptor descriptor, String beanName)
which creates a DependencyProvider
object which is implemented as
private class DependencyProvider extends DependencyObjectFactory implements Provider<Object> {
public DependencyProvider(DependencyDescriptor descriptor, String beanName) {
super(descriptor, beanName);
}
@Override
public Object get() throws BeansException {
return getObject();
}
}
where getObject()
fetches the bean from the enclosing DefaultListableBeanFactory
instance.
So, if anything, you are probably getting your exception when trying to call
fooProvider.get();
because that tries to get a Foo
bean from the context and doesn't find one.
Upvotes: 1