오늘 배운 것
- 같은 타입 Bean이 여러 개라면
- 해결 방법
- 해결 방법 우선 순위
같은 타입 Bean이 여러 개라면
같은 타입의 Bean이 여러 개인 상황부터 보자.
먼저 인터페이스가 있는 것이다.
public interface Product{
...
}
그리고 이 인터페이스를 구현한 클래스가 2개 있는 것이다.
@Component
public class KoreaProduct implements Product{
...
}
@Component
public class JapanProduct implements Product{
...
}
이 구현 클래스들에 @Component를 붙여 Bean으로 각각 등록한 것이다.
두 구현 클래스 모두 Product 타입으로 같은 타입의 클래스를 Bean으로 등록하는 것은 문제가 없다.
하지만 문제는 Product 타입의 빈을 주입 받아야 할 때 발생한다.
@SpringBootTest
public class BeanTest {
@Autowired
Product product; // 컴파일 오류 발생!!!
}
Product 타입의 Bean을 주입하려고 시도하는 것이다.
이때 'Product 타입의 Bean 객체가 하나 이상 있다' 라며 컴파일 오류가 발생한다.
스프링에서 같은 타입의 Bean 객체가 여러 개가 있다면 어떤 Bean을 가져와서 주입해야 할지 알 수 없다.
따라서 컴파일 오류가 발생하는 것이다.
같은 타입의 Bean 객체가 여러 개 있다면 스프링은 어떤 Bean 주입해야 할지 모르기 때문에 오류 발생한다!!
해결 방법
이렇게 같은 타입의 Bean 객체가 여러 개 있을 때 해결할 수 있는 방법이 3가지가 있다.
1. 등록된 Bean 이름 명시
첫번째 방법은 빈을 주입 받을 때 필드명에 등록된 Bean 이름을 사용하는 것이다.
아래와 같이 사용하는 것이다.
@SpringBootTest
public class BeanTest {
@Autowired
Product koreanProduct;
@Autowired
Product japanProduct;
}
Bean이 자동으로 등록이 될 때에는 Bean 이름은 클래스 명에서 앞글자가 소문자가 되는 것이다.
이렇게 Bean 이름을 명시하면 같은 타입의 Bean이 여러 개 있을 때 주입할 수 있는 이유는 아래와 같다.
- @Autowired를 통해 주입할 때 기본적으로 Bean type으로 Bean을 찾아 주입한다.
- 같은 타입이 여러 개라 주입할 수 없는 경우 Bean name으로 Bean을 찾아 주입한다.
이 방법은 @Autowired를 사용해서 Bean을 주입할 때만 사용할 수 있다.
그렇다면 다른 방법으로 Bean을 주입할 때에는 어떤 방법을 사용할 수 있는지 보자.
2. @Primary 사용하기
같은 타입의 Bean이 여러 개일 때 해결할 수 있는 2번째 방법은 @Primary annotation을 사용하는 것이다.
annotation의 이름으로 알 수 있듯이 이 annotation이 있는 Bean을 우선으로 주입하는 것이다.
KoreanProduct 클래스에 @Primary를 붙인다.
@Component
@Primary
public class KoreaProduct implements Product{
...
}
그리고 이전에 컴파일 오류 났던 것처럼 Bean을 주입하려고 해도 오류가 발생하지 않는다.
@SpringBootTest
public class BeanTest {
@Autowired
Product product; // 컴파일 오류 발생 X, koreanProduct Bean 주입!!
}
이렇듯 같은 타입의 Bean이 여러 개 있다면 @Primary annotation이 있는 Bean을 우선으로 주입한다.
3. @Qualifier 사용하기
같은 타입의 Bean이 여러 개일 때 해결할 수 있는 3번째 방법은 @Qualifier annotation을 사용하는 것이다.
Qualifier라는 이름에서 알 수 있듯이 Bean을 구분해주는 것이다.
클래스에 @Qualifier("빈 이름") 으로 빈 이름을 지정하는 것이다.
@Component
@Qualifier("japanProduct")
public class JapanProduct implements Product{
...
}
그리고 Bean을 사용할 때에도 @Qualifier를 붙여주는 것이다.
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("japanProduct")
Product product; // japanProduct Bean 주입!!
}
빈을 주입하고자 하는 필드에도 @Qualifier를 추가해주면 해당 빈 이름의 Bean 객체를 주입해준다.
해결 방법 우선 순위
3가지의 해결 방법에는 우선 순위가 있다.
같은 타입의 Bean이 여러 개 있을 때 KoreanProduct 객체에는 @Primary, JapanProduct 객체에는 @Qualifier가 적용되어 있다면 JapanProduct 객체가 주입된다. (@Qualifier > @Primary)
@Component
@Primary
public class KoreaProduct implements Product{
...
}
@Component
@Qualifier("japanProduct")
public class JapanProduct implements Product{
...
}
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("japanProduct")
Product product; // japanProduct Bean 주입!!
}
같은 타입의 Bean이 여러 개 있을 때 JapanProduct 객체에는 @Qualifier를 적용하고 주입 받는 곳에 KoreanProduct 객체 Bean의 이름을 명시한 경우에도 JapanProduct 객체가 주입된다. (@Qualifier > 등록된 빈 이름 명시)
@Component
public class KoreaProduct implements Product{
...
}
@Component
@Qualifier("japanProduct")
public class JapanProduct implements Product{
...
}
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("japanProduct")
Product koreanProduct; // japanProduct Bean 주입!!
}
같은 타입의 Bean이 여러 개 있을 때 KoreanProduct 객체에는 @Primary를 적용하고 주입 받는 곳에 JapanProduct 객체 Bean의 이름을 명시한 경우에는 JapanProduct 객체가 주입된다. (등록된 빈 이름 명시 > @Primary)
@Component
@Primary
public class KoreaProduct implements Product{
...
}
@Component
public class JapanProduct implements Product{
...
}
@SpringBootTest
public class BeanTest {
@Autowired
Product japanProduct; // japanProduct Bean 주입!!
}
따라서 우선 순위는 다음과 같다.
- @Qualifier
- 등록된 Bean 이름 명시
- @Primary
스프링은 거의 좁은 범위가 넓은 범위보다 우선 순위가 높다.
'TIL' 카테고리의 다른 글
24.09.12 TIL - 프로젝트 하면서 배운 팁 (3) | 2024.09.13 |
---|---|
24.09.11 TIL - MSA Service 공통 처리 (1) | 2024.09.12 |
24.09.09 TIL - Bean 수동 등록 (0) | 2024.09.10 |
24.09.06 TIL - CSRF (0) | 2024.09.08 |
24.09.05 TIL - Effective Java Item 63 (1) | 2024.09.08 |