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


138

背景:

我有一个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")

也就是说,我需要一种将HibernateDeviceDaobean 配置为自动装配的默认bean的方法,同时允许在JdbcDeviceDao带有@Qualifier注释的显式指定中同时使用the 。

我已经尝试过的:

我尝试设置属性

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。请注意,这不会影响按名称进行的显式引用,即使指定的bean未标记为自动装配候选,该名称也将得到解析。*

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

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

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

@Autowired
@Qualifier("jdbcDeviceDao")

解:

skaffman 建议尝试使用@Resource批注起作用。因此,该配置将jdbcDeviceDao的autowire-candidate设置为false,当使用jdbcDeviceDao时,我使用@Resource注释(而不是@Qualifier)来引用它:

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

如果我在代码中的100个地方使用此接口,并且想将所有界面切换到另一个实现,则不想在所有地方都更改限定符或资源注释。而且我也不想更改任何一种实现的代码。为什么没有像Guice这样的显式绑定可能性?
DanielHári17年

Answers:


134

我建议带标记Hibernate的DAO类@Primary,即(假设你使用@RepositoryHibernateDeviceDao):

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

这样,它将被选作默认的自动装配候选,而无需autowire-candidate在另一个bean上。

另外,与使用相比@Autowired @Qualifier,我发现使用它@Resource来挑选特定的豆更优雅,例如

@Resource(name="jdbcDeviceDao")
DeviceDao deviceDao;

我忘记在问题中提到我正在使用Spring 2.5(我现在已经编辑了问题),因此@Primary不是一个选择。
simon 2012年

1
@simon:是的,那很重要。@Resource按照我也建议的那样尝试注释。
skaffman,2012年

1
谢谢,资源注释解决了这个问题-现在autowire-candidate属性可以按我预期的那样工作了。
simon 2012年

谢谢!通过@Resource和指定Bean名称之间的区别是什么@Qualifier,除了前者相对于后者相对较新之外?
ass 2015年

1
@asgs使用资源注释可简化操作。您可以将其标记为依赖项注入,然后在一行中指定名称,而不是使用自动装配/限定符组合。请注意,simon的解决方案是多余的,可以删除自动装配的注释。
吉尔伯特·阿里纳斯·匕首

37

@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时,对集成测试也非常有用。


我忘记在问题中提到我正在使用Spring 2.5(我现在已经编辑了问题),因此@Primary不是一个选择。
simon 2012年

1
@simon:我相信primary=""属性较早可用。只需HibernateDeviceDao用XML 声明,并将其从组件/注释扫描中排除即可。
Tomasz Nurkiewicz 2012年

1
根据文档,该文档自3.0开始可用:static.springsource.org/spring/docs/3.1.x/javadoc-api/org/…不管怎样,我会记住下一个项目的主要注解使用Spring 3.x
simon,2012年

8

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


2
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.

0

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

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

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

可以在这里找到更详细的解释:
注入和资源以及自动装配注释

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.