본문 바로가기
코딩공부/스프링

[스프링 핵심 원리 - 기본편] 6강 - 의존관계 자동 주입(2)

by 에반셀린 2023. 4. 11.

*이 글은 개인적인 공부/기록 목적으로 올립니다.

부족하다고 느껴질 수 있는 글이니 양해 부탁드립니다.*

 

참고 영상/저작권

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com


 롬복과 최신 트렌드

필드 주입처럼 좀 편리하게 사용하는 방법은 없을까?

  • 롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면
    final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다.
    (다음 코드에는 보이지 않지만 실제 호출 가능하다.)
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
      private final DiscountPolicy discountPolicy;
}
최근에는 생성자를 딱 1개 두고, @Autowired를 생략하는 방법을 주로 사용한다.
여기에 Lombok 라이브러리의 @RequiredArgsConstructor
 함께 사용하면 기능은 다 제공하면서, 코드는 깔끔하게 사용할 수 있다.

 

조회 빈이 2개 이상일 때

@Component
public class FixDiscountPolicy implements DiscountPolicy {}

@Component
public class RateDiscountPolicy implements DiscountPolicy {}

public class Client {
  @Autowired
  private DiscountPolicy discountPolicy;
}
  • DiscountPolicy 의 하위 타입 2개를 모두 스프링 빈으로 선언하면
    NoUniqueBeanDefinitionException 오류가 발생한다.
NoUniqueBeanDefinitionException: No qualifying bean of type
'hello.core.discount.DiscountPolicy' available: expected single matching bean
but found 2: fixDiscountPolicy,rateDiscountPolicy

 

@Autowired 필드 명 매칭

  • 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Autowired
private DiscountPolicy rateDiscountPolicy

 

@Qualifier

  • 추가 구분자를 붙여주는 방법이다.
  • 주입시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것은 아니다.
  • @Qualifier 로 주입할 때 @Qualifier("mainDiscountPolicy") 를 못찾으면
    mainDiscountPolicy라는 이름의 스프링 빈을 추가로 찾는다.
  • @Qualifier 끼리 매칭
  • 빈 이름 매칭
  • NoSuchBeanDefinitionException 예외 발생

 

@Primary

  • 우선 순위를 정하는 방법이다.
  • @Autowired할 때 여러 빈이 매칭되면 @Primary 붙은 빈이 우선권을 가진다.
스프링은 자동보다 수동이, 넓은 선택권 보다는 좁은 선택권이 우선 순위가 높다.
@Primary 는 기본값처럼 동작하지만 @Qualifier는 매우 상세하게 동작하는
기능이므로
@Qualifier가 우선권이 높다.

롬복과 최신 트렌드

  • @Qualifier("mainDiscountPolicy") 로 쓰면 String이므로 컴파일 할 떄 타입 체크가 안된다.
// 애너테이션을 직접 만든다.
@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 {}

 

조회한 빈이 모두 필요할 때

  • 의도적으로 정말 해당 타입의 스프링 빈이 다 필요한 경우도 있다.
    (예를 들어서 할인 서비스를 제공하는데, 클라이언트가 할인의 종류(rate, fix)를 선택할 수 있다면...)
public class DiscountService {
    private final Map<String, DiscountPolicy> policyMap;
    private final List<DiscountPolicy> policyList;

    // 빈을 등록할 때 맵과 리스트를 주입받는다.
    @Autowired
    public DiscountService (
        Map<String, DiscountPolicy> policyMap,
        List<DiscountPolicy> policyList) {
        this.policyMap = policyMap;
          this.policyList = policyList;
    }
}
  • Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고,
    그 값으로 DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
  • List<DiscountPolicy> : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
  • 만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 Map을 주입한다.

 

자동, 수동의 올바른 실무 운영 기준

  • 편리한 자동 기능을 기본으로 사용하자
  • 그러면 수동 빈 등록은 언제 사용하면 좋을까?
    • 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서
      설정 정보에 바로 나타나게 하는 것이 유지보수 하기 좋다.
  • 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자.
    (List, Map으로 조회한 빈이 모두 필요할 때)