참고
Spring core basic 시리즈는 김영한 님의 "스프링 핵심 원리 - 기본편" 강의를 정리한 글입니다. 글에 첨부된 사진은 해당 강의의 강의 자료에서 캡쳐한 것입니다. 제 Github에만 올려뒀다가, 정보 공유와 강의 홍보(?)를 위해 블로그에도 업로드합니다. 마크다운을 잘 쓰지 못해서 가독성이 조금 떨어지는 점 양해 바랍니다.
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
다양한 의존관계 주입 방법
의존관계 주입 방법에는 크게 4가지가 있다.
- Constructort injection
- Setter injection
- Field injection
- Method injection
Constructor Injection
생성자를 통해 DI를 하는 방식. 지금까지 우리가 사용했던 방식이 바로 생성자 주입 방식이다.
특징
- 생성자 호출 시점에 단 1번만 호출되는 것이 보장된다.
- 객체 생성 시점 이후에 생성자 호출이 불가능하므로, 주입된 객체가 바뀌는 것을 막을 수 있다.
- 불변, 필수 의존관계에 사용한다.
OrderServiceImpl.java
//package, import 생략
@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;
}
}
생성자가 1개만 존재하면 @Autowired
를 생략해도 자동으로 생성자 주입이 된다.
실행 중 의존관계가 바뀌면 안되는 경우 생성자 주입을 사용하는 것이 좋은 개발 습관이다. 예를 들어 Setter injection을 사용하는 경우, 코드 유지보수를 하는 과정에서 해당 의존관계가 불변, 필수 의존관계임에도 불구하고 해당 setter를 또 호출하는 경우가 생길 수 있기 때문이다. 이런 경우에는 그럴 가능성 자체를 막아버리는 것이 좋은 개발 습관이다.
Setter injection
Setter를 통해서 DI를 하는 방식.
특징
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
- 선택, 변경 가능성이 있는 의존관계에 사용한다.
OrderServiceImpl.java
//package, import 생략
@Component
public class OrderServiceImpl implements OrderService{
//setter를 사용해야 하므로 final이 아니다.
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
먼저 orderServiceImpl 빈과 다른 빈들이 컨테이너에 등록되고, 그 다음 setter에 의해 DI가 된다. 17장에 언급했던 스프링 컨테이너의 라이프 사이클 2단계(빈 등록 -> 의존관계 주입)과 동일하다.
반면에 생성자 주입의 경우만 특별하게 빈을 등록하는 동시에 DI가 같이 일어난다는 차이점이 존재한다. 결국에는 자바 코드를 통해 DI를 하는 것이기 때문에 객체를 생성하는 과정에서 생성자가 호출되는 것은 불가피하기 떄문.
참고1: 생성자 주입의 경우는 필수/불변의 경우이지만, Setter injection의 경우는 선택적으로 DI를 할 수 있는 경우이다. 하지만
@Autowired
의 기본 동작은 주입할 대상이 없으면 오류가 발생한다. 따라서 주입할 대상이 없어도 주입할 대상이 없어도 동작하게 하려면@Autowired(required = false)
로 지정하면 된다.
참고2: 자바빈 프로퍼티, 자바에서는 과거부터 필드의 값을 직접 변경하지 않고, setXxx, getXxx 라는 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었는데, 그것이 자바빈 프로퍼티 규약이다. 이렇게 규약(일종의 표준)으로 정해놓음으로써 얻을 수 있는 장점이 많이 있다. 더 자세한 내용이 궁금하면 자바빈 프로퍼티로 검색해보자.
Field injection
필드에 바로 주입하는 방법이다.
특징
- 코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트 하기 힘들다는 치명적인 단점이 있다.
- DI 프레임워크가 없으면 아무것도 할 수 없다.
- 그러니까 사용하지 말자!
- 테스트 코드, 혹은 스프링 설정을 목적으로 하는
@Configuration
클래스에서만 특별한 용도로 사용하자.
- 테스트 코드, 혹은 스프링 설정을 목적으로 하는
OrderServiceImpl.java
//package, import 생략
@Component
public class OrderServiceImpl implements OrderService{
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
만약 위의 코드를 스프링(혹은 다른 DI 컨테이너) 없이 순수 자바 코드로만 테스트 하려고 하면? -> 불가능하다!
DI를 외부(순수 자바 테스트코드)에서 해 줄수 있는 방법이 존재하지 않기 때문이다. Setter를 추가로 만들어주던가 해야 하는데, 그러려면 테스트를 위해서 클라이언트 코드를 고치는 상황이 발생하는 것이다. 그럴거면 차라리 Setter injection을 사용하는 것이 낫다.
Method injection
일반 메서드를 통해서 주입 받을 수 있다. 즉 Setter가 아닌 메서드에도 @Autowired
를 적용할 수 있다.
특징
- 한 번에 여러 필드를 주입 받을 수 있다.
- 일반적으로 잘 사용하지 않는다. 생성자 주입이나 setter 주입으로도 충분히 가능하기 때문.
//package, import 생략
@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;
}
}
참고: 어쩌면 당연한 이야기이지만 의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다 (위의 예시 코드들의 OrderServiceImpl은
@Component
가 붙어있기 때문에 스프링 빈으로 등록된다). 스프링 빈이 아닌 Member 같은 클래스에서@Autowired
를 적용해도 아무 기능도 동작하지 않는다!
'Spring > Spring core basic' 카테고리의 다른 글
[Spring core basic] 34 - 생성자 주입을 선택하라 (0) | 2022.05.03 |
---|---|
[Spring core basic] 33 - 의존관계 자동 주입 옵션 (0) | 2022.05.03 |
[Spring core basic] 31 - 컴포넌트 스캔, 중복 등록과 충돌 (0) | 2022.05.03 |
[Spring core basic] 30 - 컴포넌트 스캔 필터링 (0) | 2022.05.02 |
[Spring core basic] 29 - 컴포넌트 스캔 범위와 기본 스캔 대상 (0) | 2022.05.02 |
댓글