0.❓문제상황❓
스프링 컨테이너에 빈 등록을 아래와 같이 한 경우를 생각해보겠습니다.
위 코드를 설명을 간단히 설명하면 아래와 같이 두 구성으로 이루어져 있는 상황입니다.
- 할인 정책
- 할인 정책에 의존성이 있는 오더 서비스
또한 할인 정책은 두 가지로 Fix, Rate 방식이 있는 상태이죠. 세 클래스 모두 @Component 어노테이션을 붙였기 때문에 스프링 컨테이너에 빈으로 등록됩니다.
이때 문제가 생깁니다. OrderServiceImpl 클래스의 생성자를 보시면 두 번째 인자로 DiscountPolicy를 받는 것을 알 수 있고 Autowired로 의존성 주입이 될 것을 예상할 수 있습니다.
그런데.. DiscountPolicy로 조회한 빈이 위 같은 상황에서는 Fix와 Rate 방식 두 가지로 조회가 될 것입니다. 이 코드를 실행시키면 스프링은 어떻게 행동할까요? 바로 아래와 같은 에러를 뱉습니다.
의존성 주입을 위해서는 하나의 빈을 매칭 시켜야 하는데 후보가 두 개가 있으니 못하겠다,, 라는 말입니다. 그렇다고 DiscountPolicy를 Fix 방식으로 하겠다!라고 하여 생성자 인자의 DiscountPolicy를 FixDiscountPolicy로 바꾼다면 DIP를 위배하게 되고 코드의 유연성이 저하되게 됩니다.
스프링은 위와 같은 문제점을 해결할 수 있도록 몇 가지 방법을 제공해주고 있습니다. 이제 알아보도록 하죠!
1.@Autowired의 매칭 보험 수단
OrderServiceImple의 생성자를 아래와 같이 슥 바꿔봅시다.
생성자에 들어갈 파라미터 이름을 슬쩍 바꿔본 것인데요. 이럴 경우 RateDiscountPolicy의 빈 이름 rateDiscountPolicy와 동일하게 되어 스프링에서 알아서 이와 매칭을 해줍니다.
즉, 의존성을 주입할 빈 후보가 여러 개일 경우 파라미터 이름과 빈 이름이 매칭 되는 것이 있는지 체크하고, 있다면 이를 주입시키는 기능을 제공합니다.
@Autowired의 매칭에 대해 간단히 정리하자면 아래와 같습니다.
- 우선 타입을 매칭한다.
- 타입 매칭을 하려는데 결과가 2개 이상인 경우? ➜ 파라미터 혹은 필드명을 가지고 매칭한다.
2.@Qualifier
@Qualifier 어노테이션은 추가로 구분자를 붙여주는 방식입니다. 사용 예시를 살펴보죠.
위와 같이 Qualifier 어노테이션에 구분자용 문자열을 넣어주면 Qualifier끼리 매칭되어 해당 빈을 주입할 수 있게 됩니다.
이때 Qualifier를 좀 더 알아보기 쉽고, 오타로 인한 실수가 런타임 대신 컴파일 에러로 나타날 수 있게끔 커스텀 어노테이션을 만들어도 좋은 방식이라고 합니다. 예시로 확인해보겠습니다.
위 같은 방식으로 커스텀 어노테이션을 만든 뒤 아래와 같이 활용해줍니다.
위와 같이 커스텀 어노테이션을 활용해주면 의미상으로도 더욱 명확해지고, Qualifer 안에 구분자용 문자열의 오타 가능성으로 인한 런타임 에러도 없앨 수 있습니다.
3.@Primary
@Primary 어노테이션을 사용하면 Autowired 하는 도중 여러 개의 후보 빈이 매칭되면 @Primary 어노테이션이 붙어있는 빈이 우선권을 가질 수 있게 해줍니다. 바로 예시를 살펴보시죠!
위와 같이 Rate방식에 @Primary 어노테이션을 써주게 되면 매칭 할 후보 빈이 Rate, Fix 두 가지임에도 불구하고 @Primary 어노테이션이 붙어있는 Rate방식이 주입됩니다.
4.💨나가며
이번 글에서는 Autowired를 해야 하는데 조회 빈이 여러 개여서 문제가 생긴 상황과 이를 해결하는 방법에 대해 알아보았습니다.
정리하자면 해당 문제 해결을 위해 다음과 같은 방식을 사용해볼 수 있을 것 같습니다.
- @Autowired의 필드, 파라미터명 매칭 기능을 사용한다.
- @Qualifier 어노테이션을 사용한다.
- @Primary 어노테이션을 사용한다.
'Spring' 카테고리의 다른 글
[Spring & Java] 왜 난 개발할 때 Runtime Exception을 써왔지? (3) | 2023.05.16 |
---|---|
[Spring] 의존성 주입과 의존성 주입 방법에 대하여. (0) | 2023.04.30 |
[Spring] Spring MVC는 어떻게 요청에 응답할까? (Dispatcher Servlet을 중심으로) (0) | 2023.03.16 |
[Spring & Dev] 웹소켓에 대하여. (1) | 2023.01.14 |
[Spring] @SpringBootApplication 에 대하여. (0) | 2022.04.12 |