※ 본 게시글은 인프런 스프링 핵심 원리 - 기본편 강의를 바탕으로 작성하였습니다.
강의 내용을 참고하여 개인적으로 정리한 글입니다.
1. 생성자 주입
@Autowired
붙이기//생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
💡 생성자가 딱 1개만 있으면
@Autowired
를 생략해도 자동 주입 가능(스프링 빈에만 해당)
2. 수정자 주입(setter 주입)
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
💡
@Autowired
의 기본 동작은 대상이 없으면 오류 발생
💡 대상이 없어도 동작하게 하려면@Autowired(required = false)
로 지정
3. 필드 주입
@Autowired
private MemberRepository memberRepository;
💡애플리케이션의 실제 코드와 관계 없이 테스트 코드
💡스프링 설정을 목적으로 하는@Configuration
같은 곳에서만 특별한 용도로 사용
4. 일반 메서드 주입
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
💡 스프링 컨테이너가 관리하는 스프링 빈이어야 동작
생성자 주입을 선택하자
스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장
1. 불변
2. 누락
final
키워드 사용 가능 → 누락 되어도 오류를 막아줌💡 생성자 주입 방식을 제외하고 모든 주입 방식은 생성자 이후에 호출되므로, 필드에
final
키워드 사용 불가 / 오로지 **생성자 주입 방식만final
키워드 사용 가능
@Autowired
만 사용하면 required
옵션의 기본값이 True
로 되어 있어서 자동 주입 대상이 없으면 오류 발생
1. @Autowired(required=false)
2. @Nullable
3. Optional<>
@Autowired(required = false)
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
//@Nullable -> 호출은 되는데 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);
출력 결과
Lombok 라이브러리가 제공하는 @RequiredArgsConstructor
기능을 사용하면 final
이 붙은 필드를 모아서 생성자를 자동으로 만듦
@Component
// @RequiredArgsConstructor 는 OrderServiceImpl 생성자와 같다
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
//생성자 자동 주입
@Autowired → 생성자가 1개이므로 @Autowired 생략 가능
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
}
@Autowired
는 타입으로 조회 → ac.getBean(DiscountPolicy.class)
같은 말
하나의 빈을 기대했지만, fixDiscountPolicy
, rateDiscountPolicy
2개가 발견되서 NoUniqueBeanDefinitionException 오류 발생
1. @Autowired 필드 명 매칭 및 정리
@Autowired
타입 매칭 시도 → 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭2. @Qualifier 사용 및 정리
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
💡 의존관계 주입시 @Qualifier를 붙여주고 등록한 이름 적기
💡 생성자 주입, setter 주입, 필드 주입 모두 사용 가능
@Autowired
public OrderServiceImpl(@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
만약 @Qualifier("mainDiscountPolicy")
찾지 못하면 mainDiscountPolicy라는 이름의 스프링 빈을 추가로 찾기
@Qualifier
끼리 매칭NoSuchBeanDefinitionException
예외 발생3. @Primary 사용 및 정리
@Primary
는 우선순위를 정하는 방법@Autowired
시에 여러 빈이 매칭되면 @Primary
가 우선권 가짐4. 우선순위
@Qualifier
가 우선순위가 높다static class DiscountService {
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) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
System.out.println("discountCode = " + discountCode);
System.out.println("discountPolicy = " + discountPolicy);
return discountPolicy.discount(member,price);
}
}
로직 분석
DiscountService
는 Map으로 만든 모든 DiscountPolicy
를 주입 받음주입 분석
Map<String, DiscountPolicy>
- Map 키에 스프링 빈의 이름 넣고, 그 값으로 Discountpolicy
타입으로 조회한 모든 스프링 빈을 담기List<DiscountPolicy>
- DiscountPolicy
타입으로 조회한 모든 스프링 빈을 담기편리한 것이 짱이다 그러면 수동은 언제 사용하는 것이 좋을까?
1. 직접 등록하는 기술 지원 객체
2. 다형성을 적극 활용하는 비즈니스 로직
DiscountService
는 의존관계 자동 주입으로 DiscountPolicy
타입의 모든 빈을 주입 받는다 이런 경우 이해를 잘 하지 못하므로 특정 패키지에 같이 묶어두고, 핵심은 딱 보고 이해가 되어야 함!