본문 바로가기

프로그래밍/Spring

[Spring] 의존관계 자동 주입

의존관계 주입 방법

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

생성자 주입

@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

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

@Component, @Autowired
이전 sec6에서 다뤘던 내용이 생성자 주입.

  • 특징
    • 생성자 호출시점에 딱 1번만 호출되는 것이 보장.
    • 불변, 필수

생성자가 딱 1개만 있으면 @Autowired생략해도 자동으로 주입됨.
빈을 등록하면서 의존관계 주입도 같이 일어남. 왜냐하면 객체를 생성하면 자동으로 생성자가 불려지게 되므로 또 어쩔수없이 의존관계주입도 같이 해줘야만함.

 

수정자 주입(setter주입)

@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;
    }
} 

스프링 라이프사이클 : 1.스프링 빈 생성 -> 2.의존관계설정
setXxxx에 @Autowired붙이면 의존관계 설정됨.
생성자 먼저, setter가 다음으로 의존관계설정됨.

  • 특징
    • 선택, 변경가능성있는 의존관계에 사용

 

필드 주입

필드에 바로 주입하는 방식

@Autowired private MemberRepository memberRepository;
  • 특징
    • 코드가 간결하지만 외부에서 변경불가능해서 테스트가 힘들다는 치명적 단점.

필드인젝션으로 순수자바코드로 테스트하려면 세터를 만들어야 함. 그럴바엔 세터에 @Autowired하는게 나음. 필드인젝션은 안쓰는게 좋음.
세터없이 필드인젝션으로 테스트하려면 스프링 다 띄우고 테스트해야함. -> 순수 자바코드를 사용한 부분적 테스트 진행이 어려움.

스프링없이 순수한 자바코드로 테스트를 진행하려 할 때,

//OrderServiceImple.java
@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);
}
//OrderServiceTest.java
@Test
void fieldInjectionTest() {
    OrderServiceImpl orderService = new OrderServiceImpl();

    //setter를 만들어 memberRepository와 discountPolicy를 넣어줘야 NullPointerException안남.
    orderService.setMemberRepository(new MemoryMemberRepository());
    orderService.setDiscountPolicy(new FixDiscountPolicy());

    orderService.createOrder(1L, "itemA", 10000);

}

Ex)createOrder() 부분 테스트를 위해 스프링을 띄우지 않고 순수 자바코드로만 테스트 하는 경우 - memberRepository를 임시로 다른걸로 바꿔서 테스트한다했을 때,
필드주입의 경우는 그냥createOrder()만 하면 memberRepository와 discountPolicy에서 NullPointerException남. 해당하는 값을 넣어줘야하는데 안넣어줬기 때문.
생성자 주입의 경우는 new OrderServiceImpl(memberRepository, discountPolicy)하면서 값을 넣어주는게 필수가 되지만 필드주입은 그렇지 않음.
따라서 memberRepository, discountPolicy 에 해당하는 값을 넣어줘야 하는데 그렇게 하려면 setter를 이용할 수 밖에 없음.(자바빈 프로퍼티 규약 참고)
따라서 다시 setter를 만들고 setter로 memberRepository값을 넣어줌. 이렇게 되면 수정자(setter)주입과 다를게 별반 없게됨. 오히려 그럴바엔 수정자(setter)주입하는게 나음.

> 참고 : 자바빈 프로퍼티, 자바에서는 과거부터 필드의 값을 직접 변경하지 않고, setXxx, getXxx과 같은 메서드를 통해 값을 읽거나 수정하는 규칙이 있음. -> 자바빈 프로퍼티 규약

 

일반 메서드 주입

  • 일반 메서드를 통해 주입받을 수 있다.
  • 특징
    • 한번에 여러 필드 주입받을 수 있음.
    • 일반적으로 잘 사용하지 않음. (보통 생성자,수정자 주입 안에서 다 해결하기 때문에 메서드 주입 사용하는 경우는 거의 없음.)

> 참고 : 의존관계 자동주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작함. 스프링 빈이 아닌 Member같은 클래스에서 @Autowired코드를 적용해도 아무 기능도 동작하지 않음.

> 참고 : @Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생. 주입할 대상이 없어도 동작하게 하려면 `@Autowired(required = false)로 지정하면 된다.