【问题标题】:Disable Spring @Autowired Provider<T> behaviour禁用 Spring @Autowired Provider<T> 行为
【发布时间】:2014-02-13 07:42:26
【问题描述】:

我的 Spring 上下文中有一个 Provider&lt;Foo&gt; 类型的 bean。我想@Autowire 那个 bean 进入另一个类。

@Autowired
private Provider<Foo> fooProvider;

但是,Spring 看到了这一点,并决定我必须在我的上下文中定义了一个 Foo 类型的 bean,并且我希望将它包装在 Provider 中,以便 Spring 可以管理它的范围。

这不是我想要的;我没有Foo,我有Provider&lt;Foo&gt;。春天抱怨:

org.springframework.beans.factory.NoSuchBeanDefinitionException: 没有为依赖项找到类型为 [my.package.Foo] 的匹配 bean:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true)}

如何让 Spring 不那么聪明,只自动装配我想要的类型?

更新

感谢您的提问。我创建了一个测试用例来说明我的问题:

@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();
    }

}

foo-context.xml文件如下:

<?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>

测试的输出:

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)

请注意,测试不会在断言上失败;它无法初始化应用程序上下文。

奇怪的是,将测试字段的声明类型从 Provider&lt;Foo&gt; 显式更改为 SimpleFooProvider 会导致测试通过而没有任何抱怨。也就是说,

@Autowired
private SimpleFooProvider fooProvider;

【问题讨论】:

  • 您的 Provider 是从 Map 类型还是 List 类型扩展而来的,如果是这样,这可能是 Spring 触发此行为的原因。
  • @BijuKunjummen 我相信是javax.inject.Provider
  • 您能在上下文中向我们展示您对Provider bean 的声明吗?我还想查看更多异常堆栈跟踪?您是在初始化时还是在尝试使用 fooProvider 时得到它?
  • 是的,你是对的@SotiriosDelimanolis。非常整洁。
  • 感谢大家的提问。我已更新我的问题以澄清问题。

标签: spring autowired


【解决方案1】:

假设Providerjavax.inject.Provider,Spring 会自动将Provider 实现自动装配到该字段中

@Autowired
private Provider<Foo> fooProvider;

例如,这是通过以下调用层次结构完成的

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)

创建一个DependencyProvider 对象,实现为

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();
    }
}

其中getObject() 从封闭的DefaultListableBeanFactory 实例中获取bean。

所以,如果有的话,你可能会在尝试调用时遇到异常

fooProvider.get();

因为它试图从上下文中获取Foo bean,但找不到。

【讨论】:

  • 这是真的,但它不会自动将 right Provider 连接到该字段中。或者至少,它不会自动连接我想要的Provider。 :) 我对是否有一种方法可以配置 Spring 不选择自动装配自己的 DependencyProvider 而是使用我已经在上下文中声明的方法感兴趣。
  • @Samwise 我必须重新阅读 CDI 规范,但我认为不应该这样做。我认为提供Provider 是 IoC 容器的工作。
  • @Samwise 据我所知,没有。
  • 很公平。通过深入研究代码,看起来DefaultListableBeanFactory.resolveDependency() 明确检查声明的类型是否为javax.inject.Provider,如果是,则使用它自己的DependencyProvider。我有一个可行的解决方法(将类型声明为我的子接口或其 impl),或者我可能会停止使用 Provider 以避免踩到 IoC 容器的脚趾。
  • @Autowired private Provider&lt;Foo&gt; fooProvider; 这里如果有多个返回类型为Foo的命名bean,我如何让提供者返回一个特定的命名bean?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 2010-10-12
  • 2014-11-12
相关资源
最近更新 更多