의존 관계 자동 주입
의존 관계 주입 4가지 방법
1. 생성자 주입
2. 수정자(setter) 주입
3. 필드 주입
4. 일반 메서드 주입
생성자 주입
- 생성자를 통해서 의존 관계를 주입
- @Autowired을 달아주면 되고, 생성자가 1개만 있으면 생략 가능(물론 스프링 빈으로 등록된 클래스일 경우만)
- 생성자 호출 시점에 오직 1번만 호출되므로 불변, 필수 의존 관계에 사용됨
수정자(setter) 주입
- 수정자 메서드를 통해서 의존 관계를 주입
- 수정자에 @Autowired
- 선택, 변경 가능성이 있는 의존 관계에 사용
필드 주입
- 필드에 바로 주입하는 방식
- 예시 : @Autowired private MemberRepository memberRepository;
- 코드가 간결하지만 변경이 불가능하여 테스트가 힘들다
- DI 프레임워크가 없으면 아무것도 못함
- 사용하지 않는 것이 답(테스트 코드나 스프링 설정을 목적으로하는 @Configuration같은 곳에서만 특별하게 사용)
일반 메서드 주입
- 한번에 여러 필드를 주입받을 수 있다는 것이 특징, 하지만 잘 사용하지 않음
이 중 생성자 주입 방법을 선택해야 한다!
=> 의존 관계는 처음 설정이 끝까지 가는 경우가 대부분이기 때문에 불변이어야 한다. 즉 바꿀 수 없게 해야한다.
setter를 통한 주입은 변경에 대해서 닫아놓을 수가 없기 때문에(public) 좋은 설계가 될 수 없다.
그리고 final 키워드는 생성자 주입을 사용할 때 필드에 달아놓을 수 있기 때문에 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.
옵션 처리
자동 주입 대상을 옵션으로 처리하는 방법
- @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨
- org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
- Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
롬북 라이브러리
롬북 라이브러리를 설치하고 @RequiredArgsConstructor을 사용하면 final 필드를 모두 모아서 생성자로 만들어줌.
그래서 코드가 훨씬 간결해진다!
조회 빈이 2개 이상
- @Autowired는 타입으로 조회함 => 중복 문제가 발생할 수 있음
- 해결 방법은 3가지
1. @Autowired 필드명 매칭 : 타입 매칭 시 중복이 있으면 필드이름, 파라미터 이름으로 빈 이름을 추가 매칭함
<기존>
@Autowired
private DiscountPolicy discountPolicy
<변경> : 필드 이름을 rateDiscountPolicy로 바꾸는 것만으로 rateDiscountPolicy라는 이름을 가진 빈이 매칭되게 해줌
@Autowired
private DiscountPolicy rateDiscountPolicy
2. @Qualifier 사용
추가 구분자를 붙여주는 방법.
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
생성자 자동 주입 예시
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
수정자 자동 주입 예시
@Autowired
public DiscountPolicy setDiscountPolicy(@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
return discountPolicy;
}
3. @Primary 사용 => 우선권을 갖게 됨
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
참고: @Qualifier와 @Primary가 겹치면 @Qualifier가 우선권이 높음
+) 어노테이션 직접 만들기
@Qualifier("mainDiscountPolicy") 이렇게 문자를 적으면 컴파일시 타입 체크가 안된다. 그럼 실수할 확률은 높아짐.
다음과 같은 애노테이션을 만들어서 문제를 해결할 수 있다.
package hello.core.annotataion;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
사용
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
생성자 자동 주입
//생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
조회한 빈 모두 필요할 경우 - List, Map 사용법
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = " + policyMap);
System.out.println("policies = " + policies);
}
}
Map : 키에 스프링 빈의 이름을 넣어주고, 그 값으로 DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
List : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
* 만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 Map을 주입한다.
------------------------
참고 : 인프런 김영한님 강의(스프링 핵심원리 기본편)
댓글