자동 주입 대상이 2개 이상일 때, @Autowired는 타입으로 조회하기 때문에 문제가 발생한다.
Ex ) GradePolicy의 구현체이자 하위 타입인 RelativeGradePolicy, AbsoluteGradePolicy가 모두 스프링 빈으로 등록이 되어 있을 때 자동으로 의존 관계를 주입할 수 없음. (선택의 기준이 없기 때문)
하위 타입으로 지정하면 해결할 수 있지만, 이는 DIP를 위배하고 유연성이 떨어지기 때문에 좋은 방법이 아니다. 또 한, 이름만 다르고 완전히 똑같은 타입의 스프링 빈이 2개 있을 때는 해결이 되지 않는다.
스프링 빈을 수동으로 등록하는 방법을 생각할 수 있지만, 의존 관계 자동 주입에서 제공하는 여러 방법이 있는데 이를 이용하는 것이 좋다.
의존 관계 자동 주입 방법 - 3가지
@Autowired
먼저 Autowired는 타입 매칭을 시도하고 빈이 여러개가 있으면, 필드 이름 -> 파라미터 이름으로 빈을 추가 매칭한다.
@Autowired
private GradePolicy gradePolicy;
------- 수정 후 -------
@Auowired
private GradePolicy RelativeGradePolicy;
@Qualifier
추가 구분자를 붙여주는 방법으로, 주입시 추가적인 정보를 제공한다. (빈 이름을 변경하지 않는다).
아래 코드에서 @Qualifier로 RelativeGradePolicy에 mainGradePolicy라는 추가적인 정보를 제공하고, 인자에 @Qualifier를 추가해주면 this.gradePolicy = gradePolicy문장을 실행할 때 RelativeGradePolicy로 연결한다.
@Component
@Qualifier("mainGradePolicy")
public class RelativeGradePolicy implements GradePolicy() {}
@Autowired
public GradePolicyImpl(StudnetRepository studnetRepository, @Qualifier("mainGradePolicy") GradePolicy gradePolicy) {
this.studentRepository = sutdentRepository;
this.gradePolicy = gradePolicy;
}
만약 mainGradePolicy를 찾지 못하면, mainGradePolicy라는 이름의 스프링 빈을 추가로 찾는다. 만약 빈도 없으면 예외가 발생한다.
@Primary
@Primary 어노테이션을 이용하여 우선권을 줄 수 있다.
@Component
@Primary
public class RelativeGradePolicy implement GradePolicy {}
@Component
public class AbsoluteGradePolicy implements GradePolicy {}
@Primary와 @Qualifier를 보면 추가 정보를 줌으로써 동작하는 것은 공통점이다. 그렇다면 어떤 어노테이션을 사용해야 할까?
-> Qualifier의 경우 모든 코드에 @Qualifier를 붙여주어야 한다는 단점이 있지만, @Primary를 사용하면 @Qualifier를 붙일 필요가 없다. 따라서 메인 DB 커넥션과 같이 자주 사용되는 DB 커넥션에는 Primary 어노테이션을 이용하고, 서브 DB 커넥션에는 Qualifier 어노테이션을 이용하면 된다.
우선순위
@Primary는 기본값 처럼 동작하고, @Qualifier는 상세하게 동작한다. 스프링에서는 자동보다는 수동이, 넓은 범위보다는 좁은 범위가 우선순위가 높다. 따라서 @Qualifier가 우선순위를 갖는다.