스프링 컨테이너에 빈을 등록하면 스프링 컨테이너가 알아서 의존관계를 맺어준다. 그런데 의존관계를 맺어줄 때 해당하는 타입의 빈이 2개 이상이라면 어떤 문제가 발생할까?
간단한 예시로 알아보도록 하자.
현재 MyRepository 인터페이스가 하나 있으며, 이를 구현하는 2개의 Repository가 있다.
public interface MyRepository {
}
@Repository
public class MemoryRepository implements MyRepository{
}
@Repository
public class JpaRepository implements MyRepository{
}
스프링 컨테이너에서 관리되는 컴포넌트에서 빈으로 등록된 MyRepository를 사용한다고 하자. @Autowired 어노테이션으로 인해 스프링 컨테이너에서 알맞은 빈을 자동으로 주입해줄 것이다.
@Component
public class MyService {
@Autowired
private MyRepository myRepository;
}
위 코드는 아무런 문제가 없을까?
MyRepository는 인터페이스이기 때문에 사용하기 위해선 실제로 구현한 MemoryRepository나 JpaRepository 중 하나를 주입해야 할 것이다.
스프링 컨테이너 입장에서는 둘 중 어떤 빈을 주입해야 할지 판단할 수 없기 때문에 에러가 발생한다. 실제로 동작해보면 다음과 같이 에러가 발생한다.
에러를 살펴보면 하나의 빈이 필요한데 2개의 빈이 발견되었다는 것을 알 수 있다.
물론 인터페이스를 주입받지 말고 구현된 하위 클래스를 지정하면 해결할 수 있지만 이는 다형성을 깨뜨리기 때문에 Repository를 교체할 경우 DIP를 위배하고 유연성이 떨어지는 단점이 있다.
@Autowired
private MemoryRepository memoryRepository;
@Autowired
private JpaRepository jpaRepository;
그렇다면 어떻게 해결해야 할까? 에러 사진을 자세히 봤다면 어느 정도 눈치를 챘을 것이다.
조회 빈이 2개 이상일 경우에는 3가지 방법으로 해결할 수 있다. 하나씩 살펴보도록 하자.
- @Autowired 필드 명 매칭
- @Qualifier 사용
- @Primary 사용
먼저 @Autowired 필드 명 매칭이다. @Autowired는 기본적으로 타입(Type)으로 조회를 시도하고, 동일한 타입의 빈이 2개 이상 존재하면 필드 이름, 파라미터 이름으로 빈을 조회한다.
따라서 다음과 같이 원하는 빈 이름을 필드 이름으로 지정해주면 된다.
@Autowired
private MyRepository memoryRepository;
//private MyRepository jpaRepository;
두 번째로 @Qualifier를 사용하는 방법이다. @Qualifier은 구분할 수 있는 정보를 추가해주는 방법이다. 의존관계 주입 시 추가적인 방법을 제공하는 것이지 빈 자체의 이름을 변경하는 것은 아니다.
빈 등록 시 @Qualifier를 붙여주고, 실제로 사용할 때 해당하는 빈을 주입받도록 해준다.
@Repository
@Qualifier("memoryRepository")
public class MemoryRepository implements MyRepository{
}
@Repository
@Qualifier("jpaRepository")
public class JpaRepository implements MyRepository{
}
------------------------------------------------------
@Autowired
@Qualifier("jpaRepository")
private MyRepository myRepository;
만약 @Qualifier의 "jpaRepository"를 못 찾는다면 실제 이름이 "jpaRepository"인 빈을 스프링 컨테이너가 추가적으로 찾는다.
마지막으로 @Primary를 사용하는 방법이다. 단어 의미 그대로 우선순위를 정하는 방법이다. 원하는 빈에 @Primary를 붙여주면 된다.
@Primary
@Repository
public class JpaRepository implements MyRepository{
}
그렇다면 3가지 방법 중 어떤 방법을 사용하는 게 좋을까?
우선 @Autowired 필드 명 매칭 방식은 어노테이션을 적용한 게 아니라 단순히 필드 명을 바꾼 것이기 때문에 오타를 낼 수 도 있고, 다른 사람이 봤을 때 알아채기가 힘들다.
또한, @Qualifier은 모든 코드에 작성해야 하는 반면, @Primary 방법은 하나만 작성하면 되기 때문에 굉장히 간단해 보인다.
따라서 기본적으로 애플리케이션 생명주기 동안 변하지 않는다면 @Primary를 사용하면 된다.
만약 애플리케이션 생명주기동안 default값으로 MemoryRepository를 사용하다가 특별한 경우에 JpaRepository를 사용해야 하는 경우가 발생한다면 MemoryRepository에 @Primary를 적용해주고 JpaRepository에 @Qualifier를 적용해주면 된다.
왜냐하면 둘 중 @Qualifier가 우선순위가 더 높기 때문이다.
스프링에서는 자동보단 수동, 넓은 범위의 선택권보다는 좁은 범위의 선택권, 좀 더 상세하게 동작하는 친구들이 우선순위가 높다.
[ Reference ]
'IT 개인 공부 > Spring' 카테고리의 다른 글
[Spring] 싱글톤 컨테이너 : CGLIB (0) | 2021.08.22 |
---|---|
[Spring] 조회한 빈(Bean)이 모두 필요할때 처리하는 방법 (0) | 2021.08.19 |
[Spring] Bean & DI & IoC 컨테이너 (0) | 2021.08.13 |
[Spring] 스프링(Spring) MVC 패턴 (0) | 2021.08.12 |
[Spring] 스프링(Spring)이란? (0) | 2021.08.12 |
댓글