본문 바로가기

Spring

Spring- 의존관계 자동 주입

의존 관계 자동 주입

 의존 관계 자동 주입은 크게 4 가지가 있다.

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

 

생성자 주입

 - 예시

package hello.core.order;

@Component
public class OrderServiceImpl implements OrderService{
    //인터페이스에만 의존 (DIP 원칙 준수)
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    //@Autowired  //생성자가 하나일 때는 @Autowired가 자동으로 삽입됨
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

	....
}

 생성자 주입의 우선 순위

 스프링 빈 등록 단계는 다음과 같다.

  1. 구성정보 클래스(xxxConfig.java)로 부터 정보를 읽어와서 스프링 컨테이너 생성.
  2. 생성한 컨테이너에 스프링 빈 등록
  3. 등록된 빈들에 의존관계 주입

 지금 보면 먼저 스프링 빈들이 등록된 후 의존관계 주입이 일어난다는 것을 알 수 있다. 그런데 빈들을 등록하려면 먼저 빈 객체를 만들어야하고 그러면 생성자가 자동 호출이 된다. 따라서 생성자 주입의 경우 다른 DI와는 달리 스프링 빈 등록 과정에서 발생한다. 즉, 생성자 주입의 우선순위가 다른 주입보다 우선적이다.

 그래서 만일 생성자도 있고 다른 주입자도 있다면 생성자 주입이 먼저 일어난 후에 다른 주입이 일어난다. 

 

 생성자 주입 @Autowired 생략

 참고로 생성자가 한 개만 있는 경우에는 생성자에 @Autowired 안써줘도 자동으로 생성자 주입이 된다.

 

수정자 주입

  - 예시

package hello.core.order;

@Component
public class OrderServiceImpl implements OrderService{
    //인터페이스에만 의존 (DIP 원칙 준수)
    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

    @Autowired  // 수정자 주입
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
        System.out.println("생성자 주입 실행1");
    }

    @Autowired  // 수정자 주입
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
        System.out.println("생성자 주입 실행2");
    }

    //@Autowired  //생성자가 하나일 때는 @Autowired가 자동으로 삽입됨
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
        System.out.println("생성자 주입 실행");
    }

  ....
}

 setter의 사용

 수정자 주입은 setter를 사용해서 DI를 진행하는 것이다. (setMemberRepository, setMemberRepository)

예제로 스프링 빈을 등록하면 생성자 주입이 먼저 일어나고, 그 후에 setter에 의해 수정자 주입이 일어난다. 

class AutoAppConfigTest{
	@Test
    void basicScan(){
    	AnnotationConfigApplicationContext ac = 
        	new AnnotationConfigApplicationContext(AutoAppConfig.class);
                ....
     }
}


// 우선순위는 생성자 주입이 먼저
생성자 주입 실행
수정자 주입 실행1
수정자 주입 실행2

 지금은 의존관계 순서를 보여주기 위해서 생성자와 수정자를 동시에 써 놓긴 했지만 어차피 싱글톤이 보장되서 OrderServiceImpl 객체에 인자 값은 동일하다. 그러므로 생성자, 수정자 둘 중 하나만 있어도 상관은 없다.

 

 수정자 주입의 사용

 일반적으로는 생성자 주입을 사용하지만 선택적, 변경 가능성이 있는 경우 수정자 주입을 사용한다. 예를 들어 orderServiceImpl 빈을 등록할 때, memberRepository 빈이 등록되어 있지 않은 경우이다. @Autowired의 경우 등록할 빈이 없는 경우 에러가 발생한다. 만일 주입할 대상이 없어도 동작하게 하려면 "@Autowired(required = false)"를 사용하면 된다.

package hello.core.order;

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

    @Autowired  // 수정자 주입
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Autowired(required = false)  // disCountPolicy 빈이 없어도 동작
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }
}

 위 경우는 discountPolicy 빈이 스프링에 등록되어있지 않더라도 @Autowired(required = false)가 있기 때문에 정상적으로 동작한다.

 

필드 주입

package hello.core.order;

@Component
public class OrderServiceImpl implements OrderService{

    @Autowired // 필드 주입
    private MemberRepository memberRepository;
    
    @Autowired // 필드 주입
    private DiscountPolicy discountPolicy;

	....
}

 그냥 위처럼 필드에 직빵으로 @Autowired를 붙여주는 것이다. 

 

 필드 주입 문제점

 주입 메소드가 필요없어서 코드가 간결하긴 한데 이렇게 하면 외부에서 변경이 불가능해서 DI 프레임워크(스프링)가 없는 경우 테스트도 불가능하다. 사용하지 말자.

 

일반 메소드 주입

 

package hello.core.order;

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

	....
}

 그냥 일반 메소드인 init( ) 메소드를 가지고 의존 관계 주입을 해주는 건데, 이것도 사실 생성자 주입이랑, 수정자 주입으로 얼마든지 대체 가능하다. 사실상 필요 없다.

'Spring' 카테고리의 다른 글

Spring- lombok  (0) 2023.01.31
Spring- 옵션 선택  (0) 2023.01.29
Spring- 컴포넌트 스캔시 필터  (0) 2023.01.26
Spring- 컴포넌트 스캔과 자동 의존관계 주입  (0) 2023.01.26
Spring- @configuration과 싱글톤  (0) 2023.01.24