1. 의존관계 주입 방법 4가지
생성자 주입
- 생성자를 통해서 의존 관계를 주입 받음.
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장.
- 불변, 필수 의존관계에 사용
- 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입
수정자 주입(setter 주입)
- setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입 받음.
- 선택, 변경 가능성이 있는 의존관계에 사용
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법
※ @Autowired 의 기본 동작은 주입할 대상이 없으면 오류가 발생.
주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
필드 주입
- 코드가 간결 하지만 외부에서 변경이 불가능해서 테스트 하기 힘듦
- DI 프레임워크가 없으면 아무것도 할 수 없음.
- 사용 안하는것을 추천
일반 메서드 주입
- 일반 메서드를 통해서 주입 받음.
- 한번에 여러 필드를 주입 받을 수 있음, 일반적으로 잘 사용하지 않는다.
※ 주입할 스프링 빈이 없어도 동작해야 할 때 옵션 사용법
package hello.core.autowired;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.lang.Nullable;
import hello.core.member.Member;
public class AuotowiredTest {
@Test
void AutowiredOption() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);
}
static class TestBean{
/* // 오류남
@Autowired
public void setNoBean0(Member noBean0) {
System.out.println("noBean0 = "+ noBean0);
}
*/
// 호출 자체가 안됨
@Autowired(required = false)
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = "+ noBean1);
}
// 호출은 되지만 null
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = "+ noBean2);
}
// 호출 되고 Optional.empty로 들어옴
@Autowired
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = "+ noBean3);
}
}
}
생성자 주입을 사용해야 함
- 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안됨.(불변)
- 수정자 주입을 하게 되면 누군가 실수로 변경할 수 도 있고, 변경하면 안되는 메서드를 열어두는 것 자체가 안좋은 설계.
- 생성자 주입을 사용하면 필드에 final 키워드를 사용가능, 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아줌.
- 생성자 주입이 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법
※ Lombok
@RequiredArgsConstructor어노테이션을 붙이면 final이 붙은 필드에 자동으로 생성자 주입을 시켜줌.
예시)
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
2. 의존관계 주입 시 조회한 빈이 2개 이상일 경우
@Autowired 는 타입(Type)으로 조회 하는데 조회된 타입이 2개일 경우 NoUniqueBeanDefinitionException 오류가 발생.
해결 방법
1) @Autowired 필드 명 매칭
- @Autowired는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가
매칭.
예시)
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = rateDiscountPolicy;
}
2) @Qualifier끼리 매칭 빈 이름 매칭
- @Qualifier 는 추가 구분자를 붙여주는 방법. 주입시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것이 아님.
예시)
조회된 첫번째 빈에 @Qualifier 적용
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
조회된 두번째 빈에 @Qualifier 적용
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
@Qualifier 사용
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
3) @Primary 사용
- @Primary 는 우선순위를 정하는 방법. @Autowired 시에 여러 빈이 매칭되면 @Primary 가 우선권을 가짐.
예시)
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy { ... }
@Component
public class FixDiscountPolicy implements DiscountPolicy { ... }
※ 우선순위
@Primary 는 기본값 처럼 동작하는 것이고, @Qualifier 는 매우 상세하게 동작한다.
스프링은 자동보다는 수동이, 넓은 범위의 선택권 보다는 좁은 범위의 선택권이 우선 순위가 높다. 여기서도 @Qualifier 가 우선권이 높다.
3. 조회한 빈이 모두 필요할때 (List, Map)
예제)
package hello.core.autowired;
import java.util.List;
import java.util.Map;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import hello.core.AutoAppConfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Grade;
import hello.core.member.Member;
public class AllBeanTest {
@Test
void findAllBean() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice = discountService.discount(member, 10000, "fixDiscountPolicy");
Assertions.assertThat(discountService).isInstanceOf(DiscountService.class);
Assertions.assertThat(discountPrice).isEqualTo(1000);
int rateDiscountPrice = discountService.discount(member, 20000, "rateDiscountPolicy");
Assertions.assertThat(rateDiscountPrice).isEqualTo(2000);
}
static class DiscountService{
// DiscountPolicy의 2개의 구현체 자동 주입
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
@Autowired
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = "+ policyMap);
System.out.println("policies = "+ policies);
}
public int discount(Member member, int price, String discountCode) {
// discountCode로 주입된 구현체 선택
DiscountPolicy discountPolicy = policyMap.get(discountCode);
return discountPolicy.discount(member, price);
}
}
}
4. 수동빈과 자동빈의 선택
- 편리한 자동 기능을 기본으로 사용.
- 직접 등록하는 기술 지원 객체는 수동 등록
- 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민
'일상의 흔적 > Study' 카테고리의 다른 글
인프런 스프링 핵심 원리 요약 : 빈스코프 - 9 (0) | 2023.03.05 |
---|---|
인프런 스프링 핵심 원리 요약 : 빈의 생명주기 콜백 - 8 (0) | 2023.03.05 |
인프런 스프링 핵심 원리 요약 : 자동 의존 관계 주입(1) - 6 (0) | 2023.03.04 |
인프런 스프링 핵심 원리 요약 : 싱글톤 패턴과 컨테이너 - 5 (0) | 2023.03.02 |
인프런 스프링 핵심 원리 요약 : 스프링의 DI와 IoC - 4 (0) | 2023.03.02 |