티스토리 뷰

다양한 DI 방법

  • 생성자
  • Setter
  • 필드
  • 일반 메소드
  1. 생성자 주입 방식 (요즘 가장 많이 쓰는 방식)
    1. 불변(final)이면서 필수인 의존관계에서 주로 사용
    2. 생성자 1개만 있다면 Autowired를 생략해도 주입됨(Bean, Component 방식 모두 해당)
    3. final 필드를 this로 접근 가능한 생성자 사용
  2. 수정자 Setter 주입
    1. 선택, 변경이 가능성이 있는 의존관계에서 주로 사용
      1. Autowired의 기본 동작은 주입할 대상이 없으며 오류가 발생하고, 주입할 동작이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
    2. 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법
      1. 자바빈 프로퍼티 규약이란 필드를 외부에서 접근할 때 Getter, Setter로 접근하자는 규칙이 있는 규약이다.
  3. 필드 주입 (안쓰는 것이 바람직)
    1. private 필드에도 가능(웬만하면 필드는 private를 쓰는 것이 당연)
    2. DI프레임워크가 없으면 아무것도 할 수 없음
    3. 코드가 꼬이고 오류가 날 확률이 매우 높음
    4. 테스트 코드 작성할 때는 가끔 쓰일 때 있음
  4. 일반 메소드 주입
    1. 한번에 여러 필드를 주입할 수 있다.
    2. 자주 사용하지 않는다.

옵션 처리

@Autowired(required = false)
public voidsetNoBean1(Member noBean1){
    System.out.println("noBean1 = " + noBean1);
}

@Autowired
public voidsetNoBean2(@Nullable Member noBean2){
    System.out.println("noBean2 = " + noBean2);
}

@Autowired
public voidsetNoBean3(Optional<Member> noBean3){
    System.out.println("noBean1 = " + noBean3);
}
  1. requried = false
    1. 메소드 자체가 호출이 안됨
  2. @Nullable
    1. null이 들어감
  3. Optional<> (자바 8 문법) → 모던 자바 인 액션 책 공부하기!
    1. Optional로 감싸주어 위 예시의 경우 Optional.empty라는 값이 출력됨
  • 위 옵션들을 생성자 자동 주입에서 일부에만 사용해 널값을 방지할 수 있음

생성자 주입이 가장 바람직한 이유

  1. 불변
    1. 대부분의 의존관계는 변하지 않고, 변하지 않는 것이 좋음
  2. 수정자 주입은 변경 가능하게 메소드를 열어 놓는 것 자체가 좋지 않음
  3. 오류를 찾아내기 수월하다
  4. final을 활용할 수 있기도 하고, final을 활용하면 생성자에서의 코드의 누락을 방지할 수 있어 오류를 찾기 더 쉽다.
  5. 프레임워크에 의존하지 않고 순수 자바를 활용할 수 있다는 것이다.

Lombok과 최신 트렌드

  • 롬복 = 라이브러리
import lombok.*;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HelloLombok {
    private String name;
    private int age;
}
  • 위와 같이 어노테이션만 쓰면 게터 세터 생성자를 자동으로 생성해줘 바로 사용 가능하다.
  • AllArgs는 모든 필드를 받아와 초기화하는 생성자라는 뜻이고, 당연히 NoArgs는 다 없는 생성자라는 뜻이다.
  • 다음 두 코드는 같은 기능을 한다.
  • Autowired와 롬복의 RequiredArgsConstructer 중 무엇을 쓰는지가 차이이다.
@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired    // 생성자 하나이면 @Autowired 생략 가능
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }

    public MemberRepository getMemberRepository(){
        return memberRepository;
    }
}
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }

    public MemberRepository getMemberRepository(){
        return memberRepository;
    }
}
  • 밑의 코드가 더 간단하지만 같은 결과가 나온다.
  • 최근에는 밑의 방식을 더 많이 사용한다.

조회 빈이 2개 이상인 문제

  • 이름이 다르고 같은 타입의 빈이 여러개 등록되면 오류가 난다.
    • 구현체가 두 개 이상인데 모두 스프링 빈으로 선언할 때를 말함
  • 구현체(하위 타입)를 빈에 직접 등록하면 DIP를 위배하는 방법이 되기에 좋은 방법이 아니다.

조회 빈이 2개 이상일 때 해결 방법

  1. @Autowired에 필드 명을 매칭
  2. @Qualifier → @Qualifier끼리 매칭 → 빈 이름 매칭
  3. @Primary 사용
  4. Autowired를 쓸 때 타입은 상위 클래스, 이름은 하위클래스의 이름을 사용
    1. Autowired는 타입 매칭 후 타입 매칭의 결과가 2개 이상일 경우 필드 명, 파라미터 명으로 빈 이름을 매칭한다.
  5. @Qualifier를 파라미터 앞에 붙여 사용
    1. 사용할 구현체 클래스 위와 자동주입할 때 사용하는 생성자의 파라미터 앞에서 사용
    2. @Qualifier는 @Qualifier끼리 찾는 용도로만 사용하는 것이 좋음
      1. Qualifier끼리 매칭
      2. 없으면 빈 이름 매칭
      3. 그래도 없으면 예외 던짐
  6. @Primary를 사용할 구현체 클래스 위에 붙여 사용
    1. 가장 많이 쓰임 → 편리하고 명확함
  • 스프링의 우선순위는 자세한 것 부터인 경우가 많다
    • @Qualifier와 @Primary를 같이 사용할 경우 @Qualifier가 우선순위를 가짐

애노테이션 직접 만들기

  • @Qualifier사용시 문자를 쓰는데 문자는 컴파일시 타입 체크가 안된다.
  • 이를 해결하기 위해 애노테이션을 직접 만들어 쓸 수 있다.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
        ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}

→ 위와 같이 작성한 후 @MainDiscountPolicy를 활용하면 @Qualifier(”mainDiscountPolicy”) 와 같은 의미를 가진다.

→ 다른 애노테이션들도 재정의할 수 있다. 웬만하면 스프링이 제공하는 애노테이션을 쓰는 것이 낫지만 Qualifier와 같은 경우 활용하면 좋을 수 있다.

'학습 내용 > 학습 정리' 카테고리의 다른 글

MySQL의 Join 기법  (2) 2024.01.11
Database Key  (0) 2024.01.11
스프링 - 2022-4-13  (0) 2022.04.13
HTTP - 2022-4-13  (0) 2022.04.13
HTTP - 2022-3-30  (0) 2022.03.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/12   »
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
29 30 31
글 보관함