빙응의 공부 블로그

[Spring]스프링 핵심 원리 - 의존 관계 주입 본문

Spring/인프런_개념

[Spring]스프링 핵심 원리 - 의존 관계 주입

빙응이 2023. 12. 28. 14:58

📝다양한 의존관계 주입 방법

[Spring]의존성 주입(DI) (tistory.com)

 

[Spring]의존성 주입(DI)

1. 의존성 주입에 대해 DI 두 객체 간의 관계를 결정해주는 디자인 패턴이다. DI는 인터페이스를 사이에 둬서 클래스 레벨에서의 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로

quddnd.tistory.com

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

이름 그대로 생성자를 통해서 의존 관계를 주입 받는 방법이다. 특징은 다음과 같다.

  • 생성자 호출시점에 딱 1번만 호출되는 것을 보장(싱글톤)
  • 불변, 필수 의존관계에 사용
    • 불변 : 프로그래밍에는 모든 가능성을 열어두지 않고 불변성을 지켜야 하는 부분이 존재한다. 
    • 필수 :  생성자에는 값을 무조건 넣는 필수 값을 사용한다. 
@Component
public class OrderServiceImpl implements OrderService{

  private final MemberRepository memberRepository;
  private final DiscountPolicy discountPoilcy;
  
  @Autowired
  public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPoilcy) {
    this.memberRepository = memberRepository;
    this.discountPoilcy = discountPoilcy;
  }

참고! 생성자가 딱 1개있는 생성자이면 @Autowired를 생략해도 자동 주입된다. 개인적으로 그냥 사용하여 표시하는게 좋을 것 같다. 

 

수정자 주입 

setter(필드 값 변경 메소드)를 이용하여 의존 관계를 주입하는 방식이다. 특징은 다음과 같다.

  • 선택, 변경 가능성이 있는 의존관계에 사용한다.
  • 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다. 
    • 자바빈 프로퍼티 규약 : set, get 메서드를 이용하여 값을 읽거나 수정하는 규칙이다. 
@Component
public class OrderServiceImpl implements OrderService {

 	private MemberRepository memberRepository;
 	private DiscountPolicy discountPolicy;
    
 	@Autowired
 	public void setMemberRepository(MemberRepository memberRepository) {
 		this.memberRepository = memberRepository;
 	}
    
 	@Autowired
 	public void setDiscountPolicy(DiscountPolicy discountPolicy) {
 		this.discountPolicy = discountPolicy;
 	}
}

참고! @Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생해 @Autowired(required = false)를 지정

 

 

필드 주입

이름 그대로 필드에 값을 넣어버리는 것, 특징은 다음과 같다.

  • 코드가 간결하여 좋을 것 같지만 외부에서 변경이 불가능해서 테스트하기 매우 힘들다.
  • DI 프레임워크가 없으면 사용 불가능
  • 걍 사용하지마! 
@Component
public class OrderServiceImpl implements OrderService {

 @Autowired
 private MemberRepository memberRepository;
 
 @Autowired
 private DiscountPolicy discountPolicy;
}

 

 

일반 메서드 주입

일반 메소드를 통해서 주입 받을 수 있다. 특징은 다음과 같다.

  • 한번에 여러 필드를 주입 받을 수 있다.
  • 일반적으로 잘 사용하지 않는다. 
@Component
public class OrderServiceImpl implements OrderService {
 private MemberRepository memberRepository;
 private DiscountPolicy discountPolicy;
 
 @Autowired
 public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
 	this.memberRepository = memberRepository;
 	this.discountPolicy = discountPolicy;
 }
}

 

 

📝옵션 처리 

주입할 스프링 빈이 없어도 동작해야 할 때가 있다.

그런데 @Autowired를 사용하면 오류가 발생한다. 

 

자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다. 

  • @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨
  • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
  • Optional<> : 자동 주입 대상이 없으면 Optional.empty가 입력된다. 
  @Test
  void AutowiredOption(){
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);

  }
  @Component
  static class TestBean{
    @Autowired(required = false)
    public void setNoBean1(Member member){
      System.out.println("member1 = "+ member);
    }
    @Autowired
    public void setNoBean2(@Nullable Member member){
      System.out.println("member2 = "+ member);
    }
    @Autowired
    public void setNoBean3(Optional<Member> member){
      System.out.println("member3 = "+ member);
    }
  }

 

 

📝다양한 의존 관계 주입 결론

생성자 주입을 사용해라!

과거에는 수정자 주입과 필드 주입을 많이 사용하였다.최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다. 

불변

클린코드에서 불변성을 지키는 것은 중요한 부분이다. 

  • 대부분의 의존관계 주입은 한번 일어나면 변경되는 일이 없다. 
  • 누군가 실수로 변경할 수도 있고, 변경하면 안되는 메소드를 열어두는 것은 좋은 클린 코드가 아니다. 
누락

프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에 다른 테스트는 의존관계 주입이 누락될 수 있다. 

그 이유는 프레임워크에 의존하기 때문에 다른 주입 사용 시 테스트하기 어렵다.

@Test
void createOrder() {
 OrderServiceImpl orderService = new OrderServiceImpl();
 orderService.createOrder(1L, "itemA", 10000);
}

 

final 키워드 

생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 그래서 생성자가 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에서 막아준다. 

@Component
public class OrderServiceImpl implements OrderService {

 private final MemberRepository memberRepository;
 private final DiscountPolicy discountPolicy;
 
 @Autowired
 public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
 	this.memberRepository = memberRepository;
 }
 //...
}
  • final 키워드를 사용하면 자바는 컴파일 시점에서 오류를 발생 시킨다.
    • 컴파일 오류는 세상에서 가장 빠르고, 좋은 오류이다. 
  • 수정자 주입이나 필드 주입은 모두 생성자 이후 호출되기 때문에, 필드에 final 키워드를 사용할 수 없다.