@Autowired로 의존 관계를 자동 주입할 때 해당 애노테이션은 타입으로 빈 조회 후 주입한다.
이 때 조회 대상의 빈이 2개 이상이면 자동 주입 시 문제가 발생할 수 있다.
이 문제를 해결하기 위한 방법은 크게 3가지로 나눌 수 있다.
예를 들어 다음과 같은 서비스가 있다고 하자.
public interface PaymentService {
void pay();
}
@Service
public class NaverPayService implements PaymentService {
@Override
public void pay() {
System.out.println("네이버 페이 결제");
}
}
@Service
public class KakaoPayService implements PaymentService {
@Override
public void pay() {
System.out.println("카카오 페이 결제");
}
}
이 경우 컴포넌트 스캔에 의해 2개의 PaymentService가 빈으로 등록될 것이다.
그리고 컨트롤러에서 해당 서비스를 다음과 같이 사용 중이다.
그렇다면 자동 주입을 받는 부분에서 스프링 컨테이너는 어떤 PaymentService를 주입해야할 지 몰라서 오류가 발생할 것이다.
@RestController
public class PaymentController {
private final PaymentService paymentService;
@Autowired
public PaymentController(PaymentService paymentService) {
this.paymentService = paymentService;
}
@GetMapping("/pay")
public String pay() {
paymentService.pay();
return "Payment processed!";
}
}
1. @Qualifier
- 위 애노테이션을 사용해 추가 구분자를 붙여주는 방법으로 실제 빈 이름을 변경하는 것은 아니다.
@Service
@Qualifier("mainPayService")
public class NaverPayService implements PaymentService {
@Override
public void pay() {
System.out.println("네이버 페이 결제");
}
}
@Autowired
public PaymentController(@Qualifier("mainPayService") PaymentService paymentService) {
this.paymentService = paymentService;
}
이 때 Qualifier로 명시한 구분자를 찾지 못한다면 해당 구분자의 빈 이름을 조회함.
→ 명확하지 않으므로 구분자로 구분할 때만 사용하는 것이 좋음
→ 해당 구분자의 빈 이름을 가지고 있는 빈이 없다면 NoSuchBeanDefinitionException 예외 발생
위 상황에서 구분자를 직접 문자로 적는데 이럴 때 문자를 잘못 적는 실수를 하면 컴파일 상황에서 에러를 찾을 수 없다.
따라서 애노테이션을 만들어서 이 문제를 해결할 수 있다.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainPayService")
public @interface MainPayService {
}
@Autowired
public PaymentController(@MainPayService PaymentService paymentService) {
this.paymentService = paymentService;
}
참고로 애노테이션은 상속 기능이 없는데 이게 가능한 이유는 스프링이 지원하는 기능임.
2. @Primary
- 우선 순위를 정하는 방법으로 여러 빈이 조회 시 해당 애노테이션이 우선 순위를 가짐
@Service
@Primary // 우선 순위
public class NaverPayService implements PaymentService {
@Override
public void pay() {
System.out.println("네이버 페이 결제");
}
}
@Service
public class KakaoPayService implements PaymentService {
@Override
public void pay() {
System.out.println("카카오 페이 결제");
}
}
만약 @Qualifier와 @Primary를 동시에 사용하고 있다면? 더 상세하게 동작하는 @Qualifier가 우선 순위.
'Spring' 카테고리의 다른 글
[Spring] 메시지, 국제화 (0) | 2024.12.27 |
---|---|
[Spring] Spring MVC (0) | 2024.12.27 |
[Spring] 서블릿과 컨트롤러 (0) | 2024.12.27 |
[Spring] 스프링 컨테이너, 빈 (0) | 2024.12.27 |
[Spring] 스프링의 이해, 스프링 부트 (0) | 2024.12.27 |