조횐된 Bean이 2개일 경우 어떤 문제가 발생 하는가?
에러 내용
No qualifying bean of type "프로젝트 이름" available: expected single matching bean but found 2: "빈이름1", 빈이름2"
해당 문제는 조회된 bean이 두개 이상 발견 됐을때 나타나는 문제이다.
문제코드
DisCountpolicy의 클래스를 상속받아 2개의 클래스를 만들었다.
그리고 @Component를 넣어 주어 Bean으로 등록 되도록 했다.
그런데 문제 가 발생한다.
그 이유는 아래의 TestCode Class 에서 생성자로 주입할 경우
Car의 자식인 CarA와 CarB가 둘다 빈에 등록 되었기 때문이다.
둘중 어느걸 넣을지 몰라 발생한 문제이다.
ChildA Class
@Component
public class CarA implements Car
{
//...
//...
//...
}
ChildA Class
@Component
public class CarB implements Car
{
//...
//...
//...
}
TestCode Class
public class TestCode {
private final Car car;
@Autowired // 여기서 에서 발생함. car 빈이 2개가 조회됨.
public OrderServiceImpl(Car car) {
this.car = car;
}
}
문제해결 방법
다음과 같은 방법으로 문제를 해결 할 수 있다.
- @Autowired 필드명
- @Qualifier
- @Primary
- 직접 어노테이션 만들기.
@Autowired 필드 명 매칭
@Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가
매칭한다.
- 타입 매칭
- 타입 매칭의 결과가 2개 이상일 때 필드 명, 파라미터 명으로 빈 이름 매칭
TestCode에 필드 명을 매칭해주어 문제 해결
@Autowired Car carA;
public class TestCode {
private final Car car;
@Autowired Car carA;
@Autowired
public OrderServiceImpl(Car carA) {
this.car = carA;
}
}
@Qualifier 사용
@Qualifier 는 추가 구분자를 붙여주는 방법이다. 주입시 추가적인 방법을 제공하는 것이지 빈 이름을 변
경하는 것은 아니다.
- @Qualifier끼리 매칭
- 빈 이름 매칭
- NoSuchBeanDefinitionException 예외 발생
- @Qualifier 는 @Qualifier 를 찾는 용도로만 사용하는게 명확하고 좋다.
CarA에 Qualifier 적용
@Component
@Qualifier("carA")
public class CarA implements Car
{
//...
//...
//...
}
CarB에 Qualifier 적용
@Component
@Qualifier("carB")
public class CarB implements Car
{
//...
//...
//...
}
TestCode에 Qualifier 적용
public class TestCode {
private final Car car;
@Autowired
public OrderServiceImpl(@Qualifier("carA")Car car) {
this.car = carA;
}
}
@Primary
@Primary 는 우선순위를 정하는 방법이다. @Autowired 시에 여러 빈이 매칭되면 @Primary 가 우선권을
가진다.
- 사용하는데 제한이 있다.
- 간편하다.
- 코드를 수정할게 없다
CarA를 우선적으로 주입할 경우
@Primary를 넣어주면 끝난다.
@Component
@Primary
public class CarA implements Car
{
//...
//...
//...
}
CarB는 손댈게 없다.
@Component
public class CarB implements Car
{
//...
//...
//...
}
TestCode
public class TestCode {
private final Car car;
@Autowired
public OrderServiceImpl(Car car) {
this.car = carA;
}
}
@Primary, @Qualifier 활용
코드에서 자주 사용하는 메인 데이터베이스의 커넥션을 획득하는 스프링 빈이 있고,
코드에서 특별한 기능 으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 생각해보자.
메인 데이터 베이스의 커넥션을 획득하는 스프링 빈은 @Primary 를 적용해서 조회하는 곳에서 @Qualifier 지정 없이
편리하게 조회하고, 서브 데이터베이스 커넥션 빈을 획득할 때는 @Qualifier 를 지정해서 명시적으로 획
득 하는 방식으로 사용하면 코드를 깔끔하게 유지할 수 있다.
물론 이때 메인 데이터베이스의 스프링 빈을 등 록할 때 @Qualifier 를 지정해주는 것은 상관없다.
우선순위
@Primary 는 기본값 처럼 동작하는 것이고, @Qualifier 는 매우 상세하게 동작한다.
이런 경우 어떤 것이 우선권을 가져갈까?
스프링은 자동보다는 수동이, 넒은 범위의 선택권 보다는 좁은 범위의 선택권이 우선 순
위가 높다.
따라서 여기서도 @Qualifier 가 우선권이 높다.
직접 어노테이션 만들기.
package hello.core.annotation;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
//중요하다.
@Qualifier("carA")
//애노테이션을 쓰면 위에있는게 전부다 동작한다.
public @interface CarA {
}
CarA에 CarA 어노테이션 지정
@Component
@CarA
public class CarA implements Car
{
//...
//...
//...
}
동일하게 TestCode에 사용
public class TestCode {
private final Car car;
@Autowired
public OrderServiceImpl(@Qualifier("carA")Car car) {
this.car = carA;
}
}
참고
'Spring > spring 기초 스터디' 카테고리의 다른 글
[Spring] Bean 생명주기 (0) | 2021.05.26 |
---|---|
[Spring] 조회한 빈을 선택해서 사용할때 (0) | 2021.05.24 |
[Spring] Lombok (0) | 2021.05.23 |
[Spring] 의존성 주입방법 2 (0) | 2021.05.17 |
[Spring] 의존성 주입방법 1 (0) | 2021.05.15 |
댓글