의존관계 주입방법은 크게 4가지이다.
- 생성자 주입
- 수정자 주입
- 필드 주입
- 일반 메서드 주입
1. 생성자 주입
가장 많이 사용하고 효용성이 있는 방법이다.
생성자 호출 시점에 1번만 호출되는 것이 보장된다. 즉 불변, 필수 의존관계에 사용한다. 가장 많이 사용되는 방식이다.
생성자가 1개만 있으면 @Autowired를 생략해도 빈이 자동으로 주입된다.
final을 사용하자
필수적이고 불변하는 관계이므로, 주입받는 객체를 final로 선언하는 방식을 주로 적용한다. 이렇게 하는 이유는, final 키워드 없이 선언 시에 생성자부분에서 오타가 발생하거나, 의존관계에 대한 부분을 작성하는 것을 누락하는 경우가 발생할 수 있기 때문이다. 이런 상황에서는 컴파일 이후에야 잘못된 부분을 찾을 수 있는데, final 키워드를 적용하면 컴파일 전에 생성자 부분에서 잘못된 부분을 미리 컴파일러가 잡아준다.
-> 생성자 주입 방식을 제외한 나머지 방식들은 모두 생성자로 생성 이후 호출되므로 필드에 final 키워드를 사용할 수 없다. 이렇게 컴파일 전에 생성자에 대한 에러를 잡아낼 수 있기 때문에 생성자 주입이 좋다.
최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다. 그 이유는 다음과 같다.
불변 : 대부분의 애플리케이션은 의존관계가 처음 실행 시 정해지고 변하지 않는다. 보통은 변하면 안된다.
누락 : 수정자 주입을 하는 경우 의존관계 주입을 해야되는 대상이 누락될 수 있다. 아래의 경우를 살펴보자.
아래 코드와 같이 수정자 주입을 해보자. CreatureServiceImpl 코드에서 Creature를 등록하기 위해 creatureRepository.register 메소드를 불러오려면, creatureRepository를 불러와야 한다(의존관계 주입을 해야한다).
테스트 코드를 실행하면 NullPointerException이 발생한다. CreatureRepository의 의존관계 설정이 안되서이다. 테스트 코드에서 CreatureRepository의 빈 껍데기라도 만들어서 넣어주던지, new 키워드를 통해 새로운 객체를 넣어주지 않으면 테스트가 실패한다. 테스트 코드에서 creatureService.register를 실행하면 creatureRepository.save가 실행되야 하는데, 테스트 환경에서는 creatureRespository에 대한 정의가 없기 때문에 NullPointerException이 발생하는 것이다.
아래와 같이 CreatureRepository를 @MockBean으로 가짜 객체로 지정하여 test를 진행하는 방법도 있다. 다만, 가짜 객체라서 CreatureRepository는 내부에서 정의된 register와 같은 메서드가 실행되는지 여부만 판단할 뿐이지, 실제 저장소인 메모리나 DB에 접근할 수는 없다.
@MockBean
CreatureRepository creatureRepository
2. 수정자 주입
수정자 주입은 선택적, 변경 가능성이 있는 의존관계에 사용한다. 이런 방식을 적용하기 위해서 @Autowired(required = false) 옵션을 적용해놓으면, 주입할 대상이 없어도 동작이 가능하다. 기본적으로 @Autowired는 주입할 대상이 없으면 오류가 발생한다.
생성자 주입이 가장 먼저 일어난다.
컨테이너에 빈을 등록할때는 크게 1. 빈등록, 2. 의존관계 주입의 단계를 거치는데, 클래스를 호출하여 등록할려면 자바 자체에서 생성자를 찾게 되므로 생성자 주입은 두 단계를 한 번에 처리하게 된다. 반면에 다른 주입 방식은 원래대로 두 단계를 거치게 된다. 따라서 만약 생성자, 수정자 주입 두 방식을 모두 적용하게 되면 항상 생성자 주입이 먼저 일어나게 된다.
3. 필드 주입
필드 자체에 그대로 주입하는 방법이다. 코드가 매우 간결하지만, 외부에서 변경할 방법이 아예 없게 되어 권장되지 않는 방식이다. 예를 들어 테스트 코드에서 creatureRepository를 DB와 연결되지 않은 로컬 레포지토리로 또는 mock 객체로 지정하여 repository상에 데이터를 넣어보고 싶어도 불가능하게 된다.
4. 메서드 주입
임의의 메서드를 만들어서 @Autowired를 적용하여 빈들을 주입받는 방식이다. 한번에 여러 필드를 주입받을 수도 있지만, 생성자, 수정자 주입으로 모두 해결 가능하기 때문에 잘 사용하지 않는다.
5. 옵션 처리
자동 주입 대상이 Null일수도 있다면, 대상을 옵션으로 처리할 수 있다. 다음 3가지 방법이 가능하다.
- @Autowired(required = false) : 적용 시, 주입 대상이 없으면 수정자 메서드가 호출되지 않는다.
- org.springframework.lang.@Nullable : 적용 시, 주입 대상이 없으면 null이 입력된다.
- Optional<> : 적용시, 주입 대상이 없으면 Optional.empty가 입력된다.
참조
1. 인프런_스프링 핵심 원리 기본편_김영한 님 강의
www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard
'Programming-[Backend] > Spring' 카테고리의 다른 글
[스프링 기초] 15. 빈 생명주기 콜백 : initMethod, destroyMethod, @PostConstruct, @PreDestroy (0) | 2021.07.11 |
---|---|
[수정중][스프링 기초] 14. 여러 개의 빈이 있을 때 자동주입하기, @Primary, @Qualifier, NoUniqueBeanDefinitionException (0) | 2021.07.07 |
[스프링 기초] 12. 컴포넌트 스캔(Component Scan) (0) | 2021.07.04 |
[스프링 기초] 11. 싱글톤(Singleton) (0) | 2021.06.30 |
[스프링 기초] 10. Bean Factory와 BeanDefinition (0) | 2021.06.30 |