본문 바로가기
Backend, Server/Spring

[스프링] 의존 관계 자동 주입, 옵션 처리, 롬북 라이브러리, 조회 빈 중복 처리, 조회한 빈 모두 가져오기

by ggyongi 2021. 12. 28.
반응형

의존 관계 자동 주입

의존 관계 주입 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을 주입한다.

 

 

------------------------

참고 : 인프런 김영한님 강의(스프링 핵심원리 기본편)

 

비전공자 네카라 신입 취업 노하우

시행착오 끝에 얻어낸 취업 노하우가 모두 담긴 전자책!

kmong.com

댓글