[Spring] 다양한 의존관계 주입 방법

SEB_BE_43_yeori316·2023년 2월 8일
0

Spring

목록 보기
11/22

다양한 의존관계 주입 방법

1. 생성자 주입
2. 수정자 주입 (setter 주입)
3. 필드 주입
4. 일반 메서드 주입


1. 생성자 주입

  • 생성자를 통해서 의존 관계를 주입 받는 방법
  • 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입
@Component
public class CoffeeService {
  private final MemberRepository memberRepository;
  private final CoffeeRepository coffeeRepository;

  @Autowired
  public CoffeeService(MemberRepository memberRepository, CoffeeRepository coffeeRepository) {
    this.memberRepository = memberRepository;
    this.coffeeRepository = coffeeRepository;
  }
}

특징

  • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장
  • 불변과 필수 의존 관계에 사용
  • 생성자가 1개만 존재하는 경우에는 @Autowired를 생략해도 자동 주입
  • NullPointerException 을 방지
  • 주입받을 필드를 final 로 선언 가능

2. 수정자 주입 (setter 주입)

  • setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존 관계를 주입하는 방법
@Component
public class CoffeeService {
  private MemberRepository memberRepository;
  private CoffeeRepository coffeeRepository;

  @Autowired
  public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
  }

  @Autowired
  public void setCoffeeRepository(CoffeeRepository coffeeRepository) {
    this.coffeeRepository = coffeeRepository;
  }
}

특징

  • 선택과 변경 가능성이 있는 의존 관계에 사용
  • 자바빈 프로퍼티 규약의 수정자 메서드(setter) 방식을 사용하는 방법
  • 생성자 주입과 차이점은 생성자 대신 set필드명 메서드를 생성하여 의존 관계를 주입
  • 수정자의 경우 @Autowired를 입력하지 않으면 실행이 되지 않는다.
    • @Component가 실행하는 클래스를 스프링 빈으로 등록합니다.
    • 스프링 빈으로 등록한 다음 의존 관계를 주입하게 되는데 @Autowired 있는 것들을 자동으로 주입하게 됩니다.

Q. 생성자는 1개일 때 @Autowired가 없어도 작동이 되는 이유는 뭘까요??

  • 스프링이 해당 클래스 객체를 생성하여 빈에 넣어야하는데 생성할 때 생성자를 부를 수 밖에 없게 됩니다.
  • 그렇기 때문에 빈을 등록하면서 의존 관계 주입도 같이 발생

3. 필드 주입

  • 필드에 @Autowired 붙여서 바로 주입하는 방법
@Component
public class CoffeeService {
  @Autowired
  private MemberRepository memberRepository;
  @Autowired
  private CoffeeRepository coffeeRepository;
}

특징

  • 코드가 간결해서 예전에 많이 사용된 방식이지만, 외부에서 변경이 불가능하여 테스트하기 힘들다는 단점이 있습니다.
  • DI 프레임워크가 없으면 아무것도 할 수 없습니다.
  • 실제 코드와 상관 없는 특정 테스트를 하고 싶을 때 사용할 수 있습니다.
  • 하지만 정상적으로 작동되게 하려면 결국 setter가 필요하게 되서 수정자 주입을 사용하는게 더 편리해집니다.

4. 일반 메서드 주입

  • 일반 메서드를 사용해 주입하는 방법입니다.

특징

  • 한번에 여러 필드를 주입 받을 수 있습니다.
  • 일반적으로 사용되지 않는다

옵션 처리

  • 주입할 스프링 빈이 없을 때 동작해야하는 경우가 있습니다.

  • @Autowired만 사용하는 경우 required 옵션 기본값인 true가 사용되어 자동 주입 대상이 없으면 오류가 발생하는 경우가 있을 수 있습니다.

  • 스프링 빈을 옵셔널하게 해둔 상태에서 등록이 되지 않고, 기본 로직으로 동작하게 하는 경우

  • 자동 주입 대상 옵션 처리 방법
    • @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출되지 않게 됩니다.
    • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력됩니다.
    • Optional<> : 자동 주입할 대상이 없으면 Optional.empty가 입력됩니다.
static class TestBean {
    @Autowired(required = false)
    public void setNoBean1(Member noBean1) {
      System.out.println("noBean1 = " + noBean1);
    }

    @Autowired
    public void setNoBean2(@Nullable Member noBean2) {
      System.out.println("noBean2 = " + noBean2);	// noBean2 = null
    }

    @Autowired
    public void setNoBean3(Optional<Member> noBean3) {
      System.out.println("noBean3 = " + noBean3);	// noBean3 = Optional.empty
    }
  }

생성자 주입을 사용해야하는 이유

  • 과거에는 수정자, 필드 주입을 많이 사용했지만, 최근에는 대부분 생성자 주입 사용을 권장

1. 불변

  • 의존 관계 주입은 처음 애플리케이션이 실행될 때 대부분 정해지고 종료 전까지 변경되지 않고 변경되서는 안됩니다.
  • 수정자 주입 같은 경우에는 이름 메서드를 public으로 열어두어 변경이 가능하기 때문에 적합하지 않습니다.
    (누군가 실수로 변경할 수도 있고, 애초에 변경하면 안되는 메서드가 변경할 수 있게 설계하는 것은 좋은 방법이 아닙니다.
  • 생성자 주입은 객체를 생성할 때 최초로 1번만 호출되고 그 이후에는 다시는 호출되는 일이 없기 때문에 불변하게 설계할 수 있습니다.

2. 누락

  • 호출했을 때는 NPE(Null Point Exception)이 발생하는데 의존관계 주입이 누락되었기 때문에 발생합니다.
  • 생성자 주입을 사용하면 주입 데이터 누락 시 컴파일 오류가 발생합니다.

3. final 키워드 사용 가능

  • 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있습니다.
  • 생성자에서 값이 설정되지 않으면 컴파일 시점에서 오류를 확인할 수 있습니다.
  • java: variable (데이터 이름) might not have been initialized
  • 생성자 주입을 제외한 나머지 주입 방식은 생성자 이후에 호출되는 형태이므로 final 키워드를 사용할 수 없습니다.

4.순환 참조

  • 순환 참조를 방지할 수 있습니다.
  • 개발하다보면 여러 컴포넌트 간에 의존성이 생기게 됩니다. (A → B를 참조하고, B → A를 참조)
  • 필드 주입과 수정자 주입은 빈이 생성된 후에 참조를 하기 때문에 애플리케이션이 어떠한 오류와 경고 없이 구동됩니다.
    • 실제 코드가 호출될 때까지 문제를 알 수 없습니다.
  • 생성자를 통해 주입하게되면 BeanCurrentlyInCreationException이 발생하게 됩니다.

※ 생성자 주입 방식의 장점 요약

  • 의존관계 설정이 되지 않으면 객체생성이 불가능
    • 컴파일 타임에 인지가 가능
    • NPE 에러 방지가 가능
  • 의존성 주입이 필요한 필드를 final 로 선언 가능
  • (스프링에서) 순환참조 감지가 가능
    • 순환 참조 시 앱구동이 실패하게 됩니다.
  • 테스트 코드 작성 용이
  • 수정자 주입이 필요한 경우가 있을 수 있지만 옵션이 필요할 때만 선택하면 됩니다.

0개의 댓글