의존관계 주입 방법 4 가지
- 생성자 주입
- 수정자 주입 (setter 주입)
- 필드 주입
- 일반 메서드 주입
기본적으로 생성자 주입을 사용하고, 가끔 옵션이 필요하면 수정자 주입을 선택한다.
생성자 주입
- 생성자에 @Autowired를 붙여 사용
- 생성자가 딱 1개 있을 경우 @Autowired를 생략해도 됨
- 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
- 불변, 필수 의존관계에 사용 (필수는 null 값이 들어가면 안되는 걸 의미)
- 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. -> 값이 설정되지 않으면 오류를 컴파일 시점에서 막아줌
- 롬복 @RequiredArgsConstructor를 사용하여 간단하게 생성자 주입을 설정할 수 있음
수정자 주입 (setter 주입)
- setter에 @Autowired를 붙여 사용
- 선택, 변경 의존관계에 사용
- 주입할 대상이 없어도 동작하도록 하려면 @Autowired(required = false)로 사용
필드 주입 (권장하지 않음)
- 필드에 @Autowired를 붙여 사용
- 코드를 간결하게 할 수 있지만
- 외부에서 변경할 수 없어 테스트하기에 어려움이 생긴다 (순수 자바코드로 테스트 할 수 없음)
- DI 프레임워크가 없으면 테스트할 수가 없다. - 다른 방법은 프레임 워크가 없어도 생성자나 setter로 주입할 수 있음
- 사용해도 되는 곳 : 애플리케이션과 관련없는 테스트 코드, 스프링 설정을 목적으로하는 @Configuration
일반 메서드 주입
- 일반 메서드에 @Autowired를 붙여 사용
- 한번에 여러 필드를 주입 받을 수 있다
- 일반적으로 잘 사용하지 않는다.
옵션처리
@Autowired(required = false)
- 주입할 빈이 없으면 아예 호출되지 않음
@Nullable
- 주입할 빈이 없으면 Null값이 들어감
Optional<>
- 주입할 빈이 없으면 Null과 비슷하게 empty로 설정됨
@Autowired(required = false)
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
@Autowired
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = " + noBean3);
}
조회 빈이 2 개 이상일 경우
@Autowired는 컨테이너에 동일한 타입의 빈을 꺼내서 주입한다. 동일한 타입의 빈이 2 개 이상이면 충돌이난다.
NoUniqueBeandefinitionException 오류
이 경우 해결 방법은 3 가지가 있다.
- @Autowired 필드명 매칭
- autowired는 타입이 같은걸 조회하고, 결과가 2 개 이상이면 파리미터명/필드명과 같은 것을 매칭함
- @Qualifier 사용
- 컴포넌트에 @qualifier("식별자명")으로 qualifire를 부여할 수 있음
- 주입할 때도 앞에 @qualifier를 붙이면 해당 qualifire를 찾아서 주입해줌
@Componenct
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy impliments DiscountPolicy{}
public class Test{
// Qualifier를 사용한 주입
public OrderServiceImpl(@Qualifier("mainDiscountPolicy") DiscountPolicy){
...
}
}
- @Primary
- 같은 타입의 빈이 여러개 일 때, primary인 빈이 우선권을 갖는다
- 컴포넌트에 @Primary를 붙여 사용한다
- primary 보단 qualifier가 우선순위가 높음 (자동보단 수동, 넓은 범위 보단 좁은 범위가 우선)
Quailifier 어노테이션 만들기
@Qualifier("qualifier 명")은 qualifier 명이 문자기 때문에 컴파일 타임에서 오류가 잡히지 않는다. (qualifier 명을 오타 내도 잡을 수 없음)
이를 어노테이션을 직접 만들어서 해결할 수 있다.
qualifier 어노테이션을 들어가보면 위와 같이 구성돼있는데, 블록친 부분을 가져와서 직접 특정 Qualifier를 만들 수 있다.
@interface에 위에서 복사한 어노테이션들을 붙이고, @Qualifier("qualifier 명")을 붙여주자.
그리고 Qualifier를 사용할 때는 @Qualifier("mainDiscountPolicy") 대신 @MainDiscountPolicy를 붙여주면 된다.
조회한 빈이 모두 필요할 때, List, Map
List나 Map으로 특정 타입의 빈을 모두 주입받을 수 있다.
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
// policyMap과 policies는 모든 DiscountPolicy를 주입받는다.
// 만약 해당 타입의 빈이 없다면 빈 Map이나 컬렉션을 주입한다.
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
}
}
'개발 > Spring' 카테고리의 다른 글
[JPA] 스프링부트 Could not write JSON: Infinite recursion 에러 해결 (0) | 2021.12.31 |
---|---|
[스프링] 스프링 핵심원리 - 빈 생명주기 콜백 (0) | 2021.12.10 |
[스프링] 스프링 핵심원리 - 싱글톤 컨테이너 (0) | 2021.12.06 |
[스프링] 스프링 핵심원리 - 스프링 컨테이너와 빈 (0) | 2021.12.05 |
[JPA] 변경감지(dirty checking)와 병합(merge) (0) | 2021.09.06 |
댓글