自动装配两个bean实现相同的接口 - 如何将默认bean设置为autowire?

时间:2012-05-10 12:30:43

标签: java spring spring-mvc autowired

背景:

我有一个Spring 2.5 / Java / Tomcat应用程序。有以下bean,在许多地方的整个应用程序中使用

public class HibernateDeviceDao implements DeviceDao

以及以下新的bean:

public class JdbcDeviceDao implements DeviceDao

配置第一个bean(包括包中的所有bean)

<context:component-scan base-package="com.initech.service.dao.hibernate" />

第二个(新)bean是单独配置的

<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao">
    <property name="dataSource" ref="jdbcDataSource">
</bean>

当启动服务器时,这会导致(当然)异常:

  

嵌套异常是org.springframework.beans.factory.NoSuchBeanDefinitionException:没有定义[com.sevenp.mobile.samplemgmt.service.dao.DeviceDao]类型的唯一bean:期望的单个匹配bean但找到2:[deviceDao, jdbcDeviceDao]

来自尝试像这样自动装配bean的类

@Autowired
private DeviceDao hibernateDevicDao;

因为有两个bean实现了相同的接口。

问题:

是否可以配置bean以便

1。我不必对已经拥有HibernateDeviceDao自动装配的现有课程进行更改

2。仍然可以像这样使用第二个(新)bean:

@Autowired
@Qualifier("jdbcDeviceDao")

即。我需要一种方法来将HibernateDeviceDao bean配置为要自动装配的默认bean,同时允许在使用JdbcDeviceDao注释明确指定时使用@Qualifier

我已经尝试过的事情:

我尝试设置属性

autowire-candidate="false"

在JdbcDeviceDao的bean配置中:

<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao" autowire-candidate="false">
    <property name="dataSource" ref="jdbcDataSource"/>
</bean>

因为Spring文档说明了

  

指示是否应该在何时考虑此bean    寻找匹配的候选人来满足另一个豆子    自动装配要求。 请注意,这不会影响显式    按名称引用,即使指定了也将得到解决    bean未被标记为autowire候选者。*

我解释为我仍然可以使用JdbcDeviceDao注释自动装配@Qualifier并将HibernateDeviceDao作为默认bean。显然,我的解释是不正确的,因为这会在启动服务器时导致以下错误消息:

  

类型[class com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]的不满意:预期至少有1个匹配的bean

来自我尝试使用限定符自动装配bean的类:

@Autowired
@Qualifier("jdbcDeviceDao")

解决方案:

skaffman's suggestion尝试@Resource注释工作。因此,对于jdbcDeviceDao,配置的autowire-candidate设置为false,当使用jdbcDeviceDao时,我使用@Resource注释(而不是@Qualifier)来引用它:

@Resource(name = "jdbcDeviceDao")
private JdbcDeviceListItemDao jdbcDeviceDao;

5 个答案:

答案 0 :(得分:118)

我建议使用@Primary标记Hibernate DAO类,即(假设您在@Repository上使用HibernateDeviceDao):

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

通过这种方式,它将被选为默认的autowire候选者,而不需要在另一个bean上autowire-candidate

此外,我发现使用@Autowired @Qualifier来挑选特定的bean,而不是使用@Resource,而不是@Resource(name="jdbcDeviceDao") DeviceDao deviceDao;

{{1}}

答案 1 :(得分:34)

@Primary怎么办?

  

表示当多个候选人有资格自动装配单值依赖关系时,应该为bean指定首选项。如果候选者中只存在一个“主”bean,则它将是自动装配的值。此批注在语义上等同于Spring XML中<bean>元素的primary属性。

@Primary
public class HibernateDeviceDao implements DeviceDao

或者,如果您希望默认使用Jdbc版本:

<bean id="jdbcDeviceDao" primary="true" class="com.initech.service.dao.jdbc.JdbcDeviceDao">

@Primary对于集成测试也很有用,因为你可以通过注释来轻松替换带有存根版本的生产bean。

答案 2 :(得分:7)

对于Spring 2.5,没有@Primary。唯一的方法是使用@Qualifier

答案 3 :(得分:0)

The use of @Qualifier will solve the issue.
Explained as below example : 
public interface PersonType {} // MasterInterface

@Component(value="1.2") 
public class Person implements  PersonType { //Bean implementing the interface
@Qualifier("1.2")
    public void setPerson(PersonType person) {
        this.person = person;
    }
}

@Component(value="1.5")
public class NewPerson implements  PersonType { 
@Qualifier("1.5")
    public void setNewPerson(PersonType newPerson) {
        this.newPerson = newPerson;
    }
}

Now get the application context object in any component class :

Object obj= BeanFactoryAnnotationUtils.qualifiedBeanOfType((ctx).getAutowireCapableBeanFactory(), PersonType.class, type);//type is the qualifier id

you can the object of class of which qualifier id is passed.

答案 4 :(得分:0)

@Resource(name =“ {your child class name}”)起作用但@Autowired有时不起作用的原因是由于它们的匹配顺序不同

@Autowire的匹配顺序
类型,限定词,名称

@Resource的匹配顺序
名称,类型,限定符

更详细的解释可以在这里找到:
Inject and Resource and Autowired annotations

在这种情况下,从父类或接口继承的不同子类会混淆@Autowire,因为它们来自同一类型。由于@Resource使用Name作为第一个匹配优先级,因此可以使用。