[스프링 의존관계 주입 방법]
1. 생성자를 통한 의존관계 주입 -> 주로 불변, 필수 의존관계에 사용된다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
System.out.println("memberRepository = " + memberRepository);
System.out.println("discountPolicy = " + discountPolicy);
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- 생성자 호출시점에 딱 한번만 호출되는것이 보장된다.
** 생성자가 딱 한 개만 있다면 Autowired 생략가능
2. 수정자 주입 -> 선택, 변경이 가능한 의존관계에 사용
required=false 조건 : 주입할 대상이 없어도 동작하게 할 경우
@Autowired(required = false)
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
System.out.println("discountPolicy = " + discountPolicy);
this.discountPolicy = discountPolicy;
}
3. 필드 주입
값의 변경이 용이하지 않다. 그래서 테스트에도 어려움이 생긴다. --> 권장하지 않는다.
* SpringBootTest : 자동으로 스프링 컨테이너를 띄워서 테스트한다.
SpringBootTest 내부에서 @Autowired 필드주입해서 사용하기도 한다.
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
4. 일반 메서드 주입 -> 한번에 여러 필드를 주입받는다 (수정자 주입과 유사)
일반적으로 생성자, 수정자 주입에서 거의 다뤄지기 때문에 잘 사용하지 않는다.
@Override
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy){
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
의존관계 주입 👉 생성자 주입을 선택하게 된다 (의존관계는 애플리케이션 종료 시점까지 변하지 않는 것이 좋다.)
생성자 주입의 장점 :
- 값을 final로 설정할 수 있다.
private final MemberRepository memberRepository; // final로 설정해 생성자에서의 코드 누락을 막는다.
private final DiscountPolicy discountPolicy;
- framework에 의존하지 않고 순수 자바의 성향을 살릴 수 있다.
- 생성자 주입 + 수정자 주입을 추가적으로 구현하면 좋다.(필드 주입은 비권장)
[자동 주입 옵션 처리]
스프링 빈이 없어도 동작해야 할 때가 있다.
단순히 @Autowired만 사용하면 required가 true로 설정되어 오류가 발생하는 문제가 생긴다!
1) @Autowired(required = false) : 자동 주입 대상이 없으면 메서드 자체가 호출되지 않는다
@Autowired(required = false) // 스프링 컨테이너에 관리되지 않는 멤버 사용
public void setNoBean1(Member member) {
System.out.println("member = " + member);
}
2) org.springframwork.lang.@Nullable : 자동 주입 대상이 없으면 null이 호출된다
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
3) Optional<> : 자동 주입 대상이 없다면 Optional.empty를 반환한다. (java8 문법)
@Autowired
public void setNoBean3(Optional<Member> noBean3){
System.out.println("noBean3 = " + noBean3);
}
[Lombok]
getter, setter, constructor, toString 등 유용한 메서드들을 자동생성해주는 플러그인
@RequiredArgsConstructor
아래와 같은 생성자를 자동으로 생성해 준다! 매우 편하다 :D
의존관계가 추가될 때도 편리 :O final 필드와 함께 사용하면 금상첨화😍
private final MemberRepository memberRepository; // final로 설정해 생성자에서의 코드 누락을 막는다.
private final DiscountPolicy discountPolicy;
//RequriedArgsConstructor가 만드는 생성자
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
[조회하는 빈이 2개 이상일 경우]
NoUniqueBeanDefinitionException이 터진다.
그렇다고 하위타입으로 지정시 DIP가 깨지고 유연성이 떨어진다.
<✨해결방법>
1) @Autowired 필드명 매칭
타입이 같은 빈이 여러 개라면 👉 필드명 / 파라미터 명을 bean 이름으로 매칭해 등록한다
2) @Qualifier
추가 구분자를 붙여주는 방식. 빈 이름이 변경되지 않는다
Qualifier 끼리 매칭 후 👉 Bean 이름 매칭
@Component
@Qualifier("mainDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {
@Component
@Qualifier("fixDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
3) @Primary
기본값처럼 동작해 등록한다. 👉 사용 빈도가 높은 빈을 우선적으로 생성해 주고 싶을 때 자주 사용한다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {
💬 @Qualifier & @Primary 함께 사용하기
사용 빈도가 높은 빈을 @Primary로 설정하고,
사용 빈도가 낮은 빈을 @Qualifier로 지정해 구현한다.
- 이 때, 우선순위는 세부적인 설정이 가능한 @Qualifier가 @Primary보다 높다.
[Annotation 만들어보기]
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
//Target~ Documented는 org.springframework.beans.factory.annotation.Qualifier에서 가져온 어노테이션
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
[조회한 빈이 모두 필요할 때] - List, Map 사용
특정 타입의 빈이 모두 필요한 경우[ex) fixDiscount, rateDiscount를 고객이 선택할 경우]에 사용된다
@Test
void findAllBean(){
ApplicationContext ac = new AnnotationConfigApplicationContext
(AutoAppConfig.class, DiscountService.class);
// 두 개 모두 설정해야 빈을 불러올 수 있다
DiscountService discountService = ac.getBean(DiscountService.class);
@RequiredArgsConstructor
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
언제 자동, 수동으로 의존관계 주입을 할까?
자동으로 의존관계 주입을 해도 OCP, DIP는 깨지지 않는다.
자동 / 수동은 상황에 맞게 쓰는 것이 중요하다
수동 빈 등록은 언제 사용해야 할까?
업무 로직 빈 : 빈 컨트롤러, 서비스, 리포지토리를 가르킴. 비즈니스 요구사항에 따라 수정됨
기술 지원 빈 : 기술적인 문제나 공통 관심사를 처리할 때 사용 (데이터베이스 연결, 공통 로그 처리) 어플리케이션 전반에 영향을 미친다.
업무 로직 빈은 주로 자동 기능을 사용하고
기술지원 빈은 수동 빈 등록을 사용해서 눈에 보이게 설정 정보에 나타나게 하는 것이 좋다.
다만 다형성이 많이 드러나는 업무 로직 빈의 경우에도 수동 빈 등록을 사용한다.
'Spring' 카테고리의 다른 글
스프링 핵심 원리 - 기본편(빈 스코프) (0) | 2022.04.28 |
---|---|
스프링 핵심 원리 - 기본편(빈 생명주기 콜백) (0) | 2022.04.27 |
스프링 핵심 원리 - 기본편(컴포넌트 스캔과 의존관계 자동 주입) (0) | 2022.04.25 |
스프링 핵심 원리(기본편) - 스프링 컨테이너와 스프링 빈 (0) | 2022.04.18 |
스프링 핵심 원리(기본편) - 객체 지향 원리 적용 (0) | 2022.04.17 |