Question

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?

Update

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;
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top