Post

Spring 스프링 핵심 원리 - 기본편 - 7. 의존관계 자동 주입

7. 의존관계 자동 주입


다양한 의존관계 주입 방법

  • 생성자 주입
  • 수정자 주입
  • 필드 주입
  • 일반 메서드 주입

생성자 주입을 권장

  • 생성자를 통해서 의존 관계를 주입 받는 방법
  • 특징
    • 생성자 호출시점에 딱 1번만 호출되는 것이 보장
    • 불변, 필수 의존관계에 사용
1
2
3
4
5
6
7
8
9
10
@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    @Autowired //생성자가 1개만 있으면 생략 가능
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}
불변
  • 대부분의 의존관계는 불변해야 한다.
  • 생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.
final 키워드
  • 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.

정리

  • 생성자 주입 방식을 선택하는 이유는 여러가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의특징을 잘 살리는 방법이기도 하다.
  • 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
  • 항상 생성자 주입을 선택해라! 그리고 가끔 옵션이 필요하면 수정자 주입을 선택해라. 필드 주입은 사용하지 않는게 좋다.

롬복과 최신 트렌드

1
2
3
4
5
6
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}
  • 롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다.

조회 빈이 2개 이상 - 문제

  • @Autowired 는 타입(Type)으로 조회한다. → 타입으로 조회하면 선택된 빈이 2개 이상일 때 문제가 발생한다.
@Primary, @Qualifier 활용
  • 코드에서 자주 사용하는 메인 데이터베이스의 커넥션을 획득하는 스프링 빈이 있고, 코드에서 특별한 기능으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 생각해보자.
  • 메인 데이터베이스의 커넥션을 획득하는 스프링 빈은 @Primary 를 적용해서 조회하는 곳에서 @Qualifier 지정 없이 편리하게 조회하고, 서브 데이터베이스 커넥션 빈을 획득할 때는 @Qualifier 를 지정해서 명시적으로 획득 하는 방식으로 사용하면 코드를 깔끔하게 유지할 수 있다.
  • 물론 이때 메인 데이터베이스의 스프링 빈을 등록할 때 @Qualifier 를 지정해주는 것은 상관없다.
  • @Primary 는 기본값 처럼 동작하는 것이고, @Qualifier 는 매우 상세하게 동작한다. 즉, @Qualifier 가 우선권이 높다.

조회한 빈이 모두 필요할 때, List, Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class AllBeanTest {
  @Test
  void findAllBean() {
      ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
      DiscountService discountService = ac.getBean(DiscountService.class);
      Member member = new Member(1L, "userA", Grade.VIP);
      int discountPrice = discountService.discount(member, 10000, "fixDiscountPolicy");
      assertThat(discountService).isInstanceOf(DiscountService.class);
      assertThat(discountPrice).isEqualTo(1000);
  }

  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);
      }
      public int discount(Member member, int price, String discountCode) {
          DiscountPolicy discountPolicy = policyMap.get(discountCode);
          System.out.println("discountCode = " + discountCode);
          System.out.println("discountPolicy = " + discountPolicy);
          return discountPolicy.discount(member, price);
      }
  }
}

정리

  • 편리한 자동 기능을 기본으로 사용하자
  • 직접 등록하는 기술 지원 객체는 수동 등록
  • 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자

References: 김영한 - [스프링 핵심 원리 - 기본편]
This post is licensed under CC BY 4.0 by the author.