동일한 인터페이스를 구현하는 두 개의 bean을 자동 배선-기본 bean을 autowire로 설정하는 방법은 무엇입니까?


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]

이처럼 콩을 자동으로 연결하려고하는 클래스에서

@Autowired
private DeviceDao hibernateDevicDao;

동일한 인터페이스를 구현하는 두 개의 Bean이 있기 때문입니다.

질문:

Bean을 구성 할 수 있습니까?

1. 나는 이미 가지고있는 기존의 클래스를 변경할 필요가 없습니다 HibernateDeviceDao를 autowire

2. 여전히 다음과 같이 두 번째 (새) Bean을 사용할 수 있습니다.

@Autowired
@Qualifier("jdbcDeviceDao")

즉, HibernateDeviceDaobean을 기본 유선으로 자동 연결되도록 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>

스프링 문서에 따르면

다른 Bean의 자동 배선 요구 사항을 충족시키기 위해 일치하는 후보를 찾을 때이 Bean을 고려해야하는지 여부를 나타냅니다. 지정된 Bean이 자동 와이어 후보로 표시되지 않은 경우에도 이름으로 명시적인 참조에 영향을 미치지 않습니다. *

주석을 JdbcDeviceDao사용하여 autowire 를 계속 사용할 수 있으며 기본 Bean을 @Qualifier가질 수 있음을 의미했습니다 HibernateDeviceDao. 서버를 시작할 때 다음과 같은 오류 메시지가 표시되므로 내 해석이 올바르지 않은 것 같습니다.

[class com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao] 유형의 충족되지 않은 종속성 : 하나 이상의 일치하는 Bean이 예상됩니다.

한정자를 사용하여 Bean을 자동 배선하려고 시도한 클래스에서 왔습니다.

@Autowired
@Qualifier("jdbcDeviceDao")

해결책:

@Resource 주석을 사용해 보겠다는 skaffman의 제안 이 효과가있었습니다. 따라서 구성에는 jdbcDeviceDao에 대해 자동 와이어 후보가 false로 설정되어 있으며 jdbcDeviceDao를 사용할 때 @Qualifier 대신 @Resource 주석을 사용하여 참조합니다.

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

코드의 100 곳 에서이 인터페이스를 사용하고 모든 것을 다른 구현으로 전환하고 싶다면 모든 곳에서 한정자 또는 리소스 주석을 변경하고 싶지 않습니다. 또한 두 구현 코드를 변경하고 싶지 않습니다. Guice처럼 명시적인 바인딩 가능성이없는 이유는 무엇입니까?
Daniel Hári

답변:


134

나는와 최대 절전 모드 DAO 클래스를 표시하는 게 좋을 것 @Primary, 즉 (당신이 사용하는 가정 @Repository에서 HibernateDeviceDao)

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

이 방법으로 autowire-candidate다른 Bean을 사용할 필요없이 기본 자동 와이어 후보로 선택됩니다 .

또한을 사용하는 대신 특정 콩을 따기 @Autowired @Qualifier위해 사용 @Resource하는 것이 더 우아하다는 것을 알았습니다.

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

Spring 2.5를 사용하고 있다는 질문에 언급하는 것을 잊어 버렸습니다 (지금 질문을 편집했습니다). @Primary는 옵션이 아닙니다.
사이먼

1
@ 사이먼 : 예, 그것은 다소 중요했습니다. @Resource내가 제안한대로 주석을 사용해보십시오 .
skaffman

1
고맙게도 리소스 주석으로 문제가 해결되었습니다. 이제 autowire-candidate 속성이 예상대로 작동합니다.
사이먼

감사! 전자가 후자보다 비교적 최신이라는 사실을 제외 @Resource하고 via 와 via를 지정하는 것의 차이점은 무엇입니까 @Qualifier?
ass

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는 옵션이 아닙니다.
사이먼

1
@ 사이먼 : primary=""속성이 더 일찍 가능 하다고 생각 합니다. HibernateDeviceDaoXML로 선언 하고 컴포넌트 / 주석 스캔에서 제외하십시오.
Tomasz Nurkiewicz

1
설명서에 따르면 3.0 이후부터 사용할 수 있습니다 : static.springsource.org/spring/docs/3.1.x/javadoc-api/org/… 어쨌든 좋은 팁, 어쨌든 다음 프로젝트의 기본 주석을 기억할 것입니다 Spring 3.x
simon

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
Name, Type, Qualifier 의 일치 순서

자세한 설명은 여기에서 찾을 수 있습니다.
주입 및 리소스 및 자동 유선 주석

이 경우 부모 클래스 또는 인터페이스에서 상속 된 다른 자식 클래스는 동일한 유형이기 때문에 @Autowire를 혼동합니다. @Resource는 Name을 첫 번째로 일치하는 우선 순위로 사용하므로 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.