*본 게시글은 김영한님 스프링 핵심 원리 기본편을 보고 이해한 내용을 바탕으로 정리한 글입니다.
의존관계 자동 주입
스프링에서는 객체 간의 의존관계를 자동으로 주입해주는 @Autowired라는 어노테이션을 제공한다.
*의존관계 주입(DI란?)
이를 통해 사용자가 외부에서 직접 의존관계를 주입해주지 않아도, 스프링에서 자동으로 의존관계를 주입해준다.
의존관계를 주입하는 방법은 총 4가지가 있다.
- 생성자 주입
- 수정자 주입(setter 주입)
- 필드 주입
- 일반 메서드 주입
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;
}
}
위와 같이 생성자를 통해서 의존 관계를 주입 받기를 원하는 경우에 쓰인다.
자동으로 스프링 컨테이너에서 getBean(MemberRepository.class)을 통해 스프링 빈을 가져와서 의존관계를 주입해준다.
만일, 생성자가 하나만 있다면 @Autowired를 생략해도 자동으로 의존관계가 주입된다.
생성자는 호출 시점에 딱 1번만 호출되기 때문에 한번 의존관계를 주입받으면 해당 객체는 불변한다.
또 객체가 불변하기 때문에 final 키워드를 통해서 객체의 값이 설정되지 않는 오류를 컴파일 시점에서 막아준다.
위 특징 때문에 보통 생성자 주입을 많이 쓴다.
*참고 : 롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어주는데, 생성자가 그것 하나라면 @Autowired도 자동으로 그 위에 붙게되고 코드가 매우 간결해진다.
수정자 주입 (setter)
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;
}
}
setter는 객체의 변화의 여지를 남겨두는 것이기 때문에 수정자 주입은 선택,변경 가능성이 있는 의존관계에 사용한다.
필드 주입
이름 그대로 필드에 바로 주입하는 방법이다.
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
코드가 간결해서 좋지만, DI 프레임워크 없이는 아무것도 못하기 때문에 잘 사용되지 않는다.
일반 메서드 주입
일반 메서드를 통해서 주입 받는 방법이다.
setter가 아닌 다른 일반 메서드를 사용하는 것이다.
잘 사용되지 않는다.
등록되어있지 않은 스프링 빈을 주입받을 때
기본적으로 오류가 난다.
등록되어있지 않은 스프링빈을 주입 받아도 오류가 나지 않게끔, 즉, Null값이 와도 되게끔 허용하는 방법에 대하여 알아보자.
1. @Autowired(required = false)
자동 주입할 대상이 없으면 수정자 메서드 자체가 호출이 안된다.
2. null이 와도 되는 매개 변수 앞에 @Nullable 어노테이션
자동 주입할 대상이 없으면 null이 입력된다.
3.null이 와도 되는 매개 변수 타입을 Optional<>
자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
조회된 빈이 2개 이상일 때
자동 주입 할 빈을 찾을 때 클래스 타입으로 조회한다.
OCP 원칙을 위해 인터페이스를 통해 의존성 주입을 받고, 그 인터페이스를 상속받은 스프링 빈이 두개 이상일 때는 조회되는 빈이 2개이기 때문에 오류가난다.
그렇다면 의존성 주입을 받을 때 생성자의 타입을 더 하위 타입으로 지정해야하는 것일까?
이는 DIP를 위해하고 유연성이 떨어진다.
이를 해결하는 방법 3가지를 알아보자.
1. @Autowired 필드 명 매칭
@Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
2. @Qualifier
@Qualifier("이름") 을 빈 등록 시 빈에 붙여주고, 주입 시에 파라미터 앞에 똑같이 @Qualifier("이름")을 붙여준다.
그러면 @Qualifier끼리 우선 매칭하고 이후에 빈 이름 매칭을 하고 그래도 없으면 오류를 낸다.
3.@Primary
@Primary을 빈 등록 시 빈에 붙여주면 해당 스프링 빈에 우선권을 두어 의존성 주입을 한다.
@Qualifier와 @Primary가 둘다 있으면, @Qualifier가 더 우선권이 높다.
훨씬 상세히 이름까지 정해주니까 보다 이런 경우 보다 구체적인 쪽이 더 우선권을 갖는다.
예를 들어서, 자주 사용하는 메인 데이터베이스 커넥션을 획득하는 스프링 빈이 있고, 코드에서 특별한 기능으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 생각해보자.
이 때는 메인 데이터베이스의 커넥션을 휙득하는 스프링 빈은 @Primary를 적용해서 코드를 간단히 하고, 서브 데이터베이스 커넥션을 휙들할 때는 @Qualifier를 사용해서 명시적으로 휙득하는 방식으로 사용하면 코드가 깔끔해진다.
한 타입의 스프링 빈이 다 모두 의존관계 주입이 필요할 때
의도적으로 정말 해당 타입의 스프링 빈이 다 필요한 경우도 있다.
이럴 때는 의존 관계 주입을 받는 변수의 타입을 Map<String, 해당 타입> 변수명 혹은 List<해당 타입> 변수명으로 하면 해당 타입으로 빈을 조회한 것이 두개 이상 나와도 해당 자료구조에 빈들이 모두 들어가게 된다.
'서버 > 스프링' 카테고리의 다른 글
[Spring] 빈 스코프 prototype (0) | 2022.08.28 |
---|---|
[Spring] 빈 생명주기 콜백 (0) | 2022.08.28 |
[Spring] 스프링 컨테이너와 스프링 빈 (0) | 2022.08.26 |
[Spring] Ioc(제어의 역전)와 DI(의존성 주입)의 개념과 그 차이 (4) | 2022.08.05 |
[Spring] Spring @ResponseBody를 사용하여 API 생성하기 (0) | 2022.08.04 |