@Autowired는 DI시 생성자, 수정자, 필드, 메소드 등에 붙는 어노테이션이다. @Autowired는 스프링 컨테이너에 등록되어 빈에서 타입이 같은 빈을 매칭하고, 이 때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Autowired
private CallRepository callRepository;
같은 타입의 빈이 두 개가 있을 경우 스프링에서는 오류를 반환한다. 만약 CallRepository의 타입의 객체가 phoneCallRepo와 computerCallRepo 둘이 등록되어 있다고 하자. 그러면 스프링에서는 NoUniqueBeanDefinitionException 예외가 발생한다.
NoUniqueBeanDefinitionException: No qualifying bean of type
'hello.core.call.CallRepository' available: expected single matching bean
but found 2: phoneCallRepo, computerCallRepo
이렇게 하나의 빈을 기대하였지만 두 개의 빈이 있다고 오류가 난다.
그러면 CallRepository말고 phoneCallRepo와 computerCallRepo 각각 빈에 등록하는 것은 DIP를 위반 즉, 구체화에 의존하는 것이기 때문에 문제가 되고 유연성이 떨어진다. 이러한 문제를 해결하는 다양한 방법이 존재한다.
@Autowired는 빈을 매칭할 때 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름츨 추가 매칭한다.
@Autowired
private CallRepository phoneCallRepo
이와 같이 처음 CallRepository 타입을 매칭하고 빈이 여러개 있으면 필드 이름과 같은 빈인 PhoneCallRepository를 주입한다.
따라서 타입 매칭 -> 필드명 매칭 -> 파라미터 매칭 순으로 매칭을 시도한다. 이 때 주의할 점은 타입 매칭 시 여러 빈이 있다면 다음 매칭을 추가적으로 시도하는 것이다.
@Qualifier는 추가 구분자를 붙여주는 방법이다. 주입시 추가적인 방법을 제공하는 것이지 등록할 빈의 이름을 변경하는 것이 아니다.
@Component
@Qualifier("mainCallRepository")
public class PhoneCallRepository implements CallRepository{}
@Component
@Qualifier("computerCallRepository")
public class ComputerCallRepository implements CallRepository{}
주입 시 @Qulifier를 붙여주고 등록한 이름을 적어준다.
@Service
@RequiredArgsConstructor
public class CallService {
@Qualifier("mainCallRepository")
private final CallRepository callRepository;
private final CallRepository callRepository2;
// constructor injection
public CallService(CallRepository callRepository, @Qualifier("computerCallRepository") CallRepository callRepository2) {
this.callRepository = callRepository;
this.callRepository2 = callRepository2;
}
}
이렇게 @Qualifier는 @Qulifier를 찾아 빈을 매칭시켜준다. 그러나 만약 해당하는 @Qulifier를 못찾는다면 스프링 빈에서 같은 이름의 빈을 찾기는 하나 이는 권장하지 않는 방법이라고 한다.
@Primary는 여러 빈이 매칭되면 @Primary가 붙는 빈이 더 우선권을 갖는다는 어노테이션이다.
@Component
@Primary
public class PhoneCallRepository implements CallRepository{}
@Component
public class ComputerCallRepository implements CallRepository{}
이렇게 되면 PhoneCallRepository가 ComputerCallRepository보다 더 우선권을 갖게 되어 PhoneCallRepository가 주입이 된다.
@Primary는 많이 사용하고 주요한 빈일 경우 사용하고 서브적인 역할을 하는 빈은 @Qulifier를 사용하면 코드를 깔끔하게 유지할 수 있다고 한다.
만약 @Primary와 @Qualifier 둘 중 우선순위가 높은건 상세하고 수동적으로 적용하는 @Qualifier가 우선순위가 높다.