如果存在具有手动设置属性的同一类的另一个bean,则不会发生自动装配

时间:2012-02-28 16:01:20

标签: java spring dependency-injection

我想通过xml配置文件创建两个相同类的bean。该类有一个带有注释的setter,可以用spring填充。在其中一个bean定义中,我还手动提供值以覆盖注释中的一个。但是当我这样做时,spring不再处理注释布线。

以下是展示此效果的最小代码,为简单起见使用了@Value,但与@Autowired相同:

import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;

public class AutowireTest {
  public String testField;

  @PostConstruct
  public void init() {
    if (testField == null)
      throw new RuntimeException("FAIL");
  }

  @Value("default")
  public void setTestField(String testField) {
    this.testField = testField;
  }
}

和spring config:

<bean id="au_test1" class="AutowireTest">
  <property name="testField" value="manual"/>
</bean>

<bean id="au_test2" class="AutowireTest"/>

如果我删除<property name="testField" value="manual"/>,则两个bean都会收到“默认”。如果它在那里第二个bean抛出一个异常。我查看了spring代码,AutowiredAnnotationBeanPostProcessor正在使用injectionMetadataCache,其中类是注入元数据的键,这意味着为一个bean设置特定属性会禁用其他实例的自动装配。

知道为什么会这样吗?如何实现类似的效果,不一定是字符串值,也适用于对象bean?

编辑: 我希望有一个属性,有多个匹配的候选人。一个标记为primary。除非我通过xml手动指定候选者,否则我希望将主要的候选者连接起来。我最初的方法是使用@Autowired这个属性,但由于这不起作用,我正在寻找替代方案。出于某种原因,我不想使用bean继承。

EDIT2: 如果我交换这两个bean定义,则不会出现问题。该属性自动装配好,直到第一次检测到手动覆盖。这意味着这不是一个意图特征,因为它可能导致奇怪且难以检测某些项目中的错误,其中依赖项未按预期连接。

2 个答案:

答案 0 :(得分:1)

如果你看一下AutowiredAnnotationBeanPostProcessor的javaDocs,就会有一条说明:

  

“注释注入将在XML注入之前执行;因此   后一种配置将覆盖前者的有线属性   通过两种方法。“

这意味着在xml中使用和设置“testField”的属性值设置bean属性会产生两个新bean,其中一个设置了testField,另一个设置为null。

它是一个或另一个。您既可以使用自动装配类,也可以在xml中手动设置,但不能同时执行这两种操作。我猜测当你删除属性spring时只是将同一个自动装配的bean设置为两个名称。

手动设置同一类的bean后,可以使用@Qualifier("au_test1")注释将它们注入其他bean

答案 1 :(得分:0)

在我看来,这是我通过jira提交的春天的错误。要解决此问题,必须将注释从setter移动到field。 Setter必须留下来允许手动覆盖酒店。应该将可能的setter逻辑(我知道应该避免这种情况)移到@PostConstruct。改编自以下原始代码:

import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;

public class AutowireTest {
  @Value("default")
  public String testField;

  @PostConstruct
  public void init() {
    if (testField == null)
      throw new RuntimeException("FAIL");
  }

  public void setTestField(String testField) {
    this.testField = testField;
  }
}