티스토리 뷰
[Spring] 빈이 중복 조회가 된다면? : @Autowired, @Qualifier, @Primary 의존성 주입의 다양한 접근 방법
zi존코딩맨 2023. 12. 19. 10:18
에러 발생
Spring 서버를 작동하려고 하는데 아래와 같은 오류가 발생한적 있는가?
예시 상황
// 인터페이스 구현1
@Component
public class EmailService implements NotificationService { ... }
// 인터페이스 구현2
@Component
public class SMSService implements NotificationService { ... }
// 다른 곳에서 어디선가 인터페이스에 빈 주입
@Autowired
private NotificationService service;
org.springframework.beans.factory.UnsatisfiedDependencyException
또는
NoUniqueBeanDefinitionException
해당 오류는 2가지의 상황이 있을 때 나타나는 에러이다.
- 의존성 주입 문제가 있는 경우 : 빈을 생성할 때 해당 빈이 필요로 하는 의존성을 찾지 못한 경우에 발생합니다.
- 여러 빈 (2개 이상의 빈)이 해당 타입의 의존성을 가지고 있는 경우 : 같은 타입의 여러 빈이 존재하여 어떤 빈을 주입해야 할지 결정할 수 없는 경우에도 발생할 수 있다.
이 글은 Spring에서 어떤 빈을 사용할지 모르는 경우인! 같은 빈이 2개이상 일 때 해결하는 방법을 작성하려고 한다.
@Autowired 필드 명 매칭
@Autowired는 타입(자료형) 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭합니다.
기존 코드 (에러 상황)
@Autowired
private NotificationService service;
필드 명을 빈 이름으로 변경
@Autowired
private NotificationService SMSService;
이러한 경우는 NotificationService의 타입으로 2가지 빈 등록(EmailService, SMSService)이 되었는데
타입 매칭으로는 중복이 발생 하므로 필드명, 파라미터 명으로 빈 이름을 매칭하였습니다.
그래서 SMSService라는 필드명을 가진 것에 빈 주입이 되어서 빈 중복에 대해 의존성 주입에 성공하였습니다.
@Qualifier 사용
@Qualifier는 추가 구분자를 붙여주는 방법입니다.
주입시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것이 아닙니다.
그래서 @Qualifier는 @Qualifier를 찾는 용도로만 사용하는게 명확하고 좋습니다.
빈 등록 시 사용 예시 (생성자 주입)
// 인터페이스 구현1
@Component
@Qualifier("mainService")
public class EmailService implements NotificationService { ... }
// 인터페이스 구현2
@Component
@Qualifier("subService")
public class SMSService implements NotificationService { ... }
// 다른 곳에서 어디선가 인터페이스에 빈 생성자 주입
@Autowired
private UserServiceImpl(MemberRepository memberRepository,
@Qualifier("mainService") EmailService emailService){
this.memberRepository = memberRepository;
this.emailService = emailService;
}
만약에 인터페이스를 구현했을 때 @Qualifier를 해주고 생성자에서 작성해둔 @Qualifier("mainService")를 못 찾으면 어떻게 될까?
그렇다면 mainService라는 이름의 스프링 빈을 추가로 찾게 됩니다.
Qualifier 탐색 순서
- @Qualifier 끼리 매칭
- 빈 이름 매칭
- NoSuchBeanDefinitionException 발생
@Primary 사용
@Primary는 우선순위를 정하는 방법입니다.
@Autowired 할 떄 여러 빈이 조회된다면 @Primary가진 빈이 우선권을 가집니다.
이 어노테이션 사용 예시로는 데이터베이스 커넥션에 대해 관련이 있습니다.
주 DB를 조회할 때 해당 커넥션에 @Primary를 추가하면 되고 서브 DB를 조회할 때는 @Primary를 안한다면 주 DB 커넥션에 우선권을 가지게 됩니다.
우선 순위 주입
// 인터페이스 구현1
@Component
public class EmailService implements NotificationService { ... }
// 인터페이스 구현2
@Component
@Primay
public class SMSService implements NotificationService { ... }
// 다른 곳에서 어디선가 인터페이스에 빈 생성자 주입
// ! NotificationService는 SMSService로 주입되었다.
@Autowired
private UserServiceImpl(MemberRepository memberRepository, NotificationService service){
this.memberRepository = memberRepository;
this.service = service;
}
그럼 @Primay와 @Qualifier중 어떤 것을 사용해야 하나!?
저는 개인적으로 @Primary 사용하는 것을 선호 합니다.
@Primary 어노테이션 하나로 코드도 깔끔해지며 무엇보다 편하기 때문입니다.
반대로 @Qualifier의 단점은 주입 받을 때 연결 시키기 위해서 모든 코드에 @Qualifier를 붙여주어야 한다는 점입니다.
@Primary를 사용하면 붙일 필요가 없는데 말이져
@Primary와 @Qualifier 활용
코드에서 자주 사용하는 메인 데이터베이스의 커넥션을 획득하는 스프링 빈이 있고, 코드에서 특별한 기능으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 생각해보자.
메인 데이터베이스의 커넥션을 획득하는 스프링 빈은 @Primary 를 적용해서 조회하는 곳에서 @Qualifier 지정 없이 편리하게 조회하고,
서브 데이터베이스 커넥션 빈을 획득할 때는 @Qualifier 를 지정해서 명시적으로 획득 하는 방식으로 사용하면 코드를 깔끔하게 유지할 수 있다.
물론 이때 메인 데이터베이스의 스프링 빈을 등록할 때 @Qualifier 를 지정해주는 것은 상관없다.
그럼 @Primary와 @Qualifier를 같이 사용하면??
@Primary는 기본 값 처럼 동작하는 것이고, @Qualifier는 매우 상세하게 동작합니다.
스프링은 자동보다는 수동이, 넓은 범위의 선택권 보다는 좁은 범위의 선택권이 우선순위를 갖습니다.
따라서 @Qualifier가 우선권이 높습니다.
Tip) @Qualifier를 무분별하게 남발하는 것보다는 Annotation을 만들어서 사용하는게 좋습니다. 어노테이션 만드는 법은 다음에
감사합니다.
'백엔드 > 🌸Spring' 카테고리의 다른 글
[Spring] Spring Boot3에 Swagger 적용하기 (6) | 2024.10.17 |
---|---|
[Spring] ThreadLocal : 동시성을 해결하는 물품 보관소 (0) | 2024.05.10 |
[Spring] ArgumentResolver : @Pathvariable 요청 정보 가져오기 (0) | 2024.04.24 |
[Spring] 첨부파일과 여러 데이터 전송하기 (0) | 2024.04.10 |
[Spring] Validation : 데이터 유효성 검사의 핵심 요소 (0) | 2024.03.27 |
- Total
- Today
- Yesterday
- 네트워크
- Front
- Cors
- 프로세스
- 개발환경
- 데이터 베이스
- 개발자
- git
- JavaScript
- Mac
- java
- DBeaver
- 프론트
- 깃허브 액션
- Fetch
- 디자인패턴
- Spring Security
- 개발
- 비동기
- 템플릿
- 오라클
- aws
- spring
- 코딩테스트
- 개발블로그
- 자바스크립트
- AJAX
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |