
이 글은 인프런에서 김영한 님의 "스프링 핵심원리 - 기본편"을 수강 후 공부한 내용을 정리한 게시글입니다. 부족한 부분이 있다면 언제든 지적 부탁드립니다.
다양한 의존 관계 주입 방법에 대해서는 “이전 게시물”을 확인해주세요.
이전 게시물 - 의존관계 자동주입 1
✏️ 자동 주입 대상을 옵션으로 처리
주입할 스프링 빈이 없어도 동작해야 할 때가 있다.
그런데 @Autowired 만 사용하면 required 옵션의 기본값이 true 로 되어 있어서 자동 주입 대상이 없으면 오류가 발생한다. 이를 해결하기 위해서는 아래와 같이 3가지 방법을 가지고 옵션처리하여 해결할 수 있다.
아래 예시에서 Member 는 스프링 빈이 아니다.
- @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨
- //호출 안됨 @Autowired(required = false) public void setNoBean1(Member member) { System.out.println("setNoBean1 = " + member); } //결과 없음
- org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
- //null 호출 @Autowired public void setNoBean2(@Nullable Member member) { System.out.println("setNoBean2 = " + member); } //결과 setNoBean2 = null
- Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
- //Optional.empty 호출 @Autowired(required = false) public void setNoBean3(Optional<Member> member) System.out.println("setNoBean3 = " + member); } //결과 setNoBean3 = Optional.empty
✏️ 생성자 주입을 선택하자!
- 생성자 주입 방식을 선택하는 이유는 여러가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이기도 하다.
- 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
- 항상 생성자 주입을 선택해라! 그리고 가끔 옵션이 필요하면 수정자 주입을 선택해라. 필드 주입은 사용하지 않는 게 좋다.
✏️ 롬북 Lombok
생성자 주입을 작성하는 과정에서 비슷한 코드르 여러번 작성하게 된다.
다 형식이 정해져 있는 코드들이기때문에 이를 롬북이라는 라이브러리에서 코드 작성을 간소화 시켜준다.
- @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다. (다음 코드에는 보이지 않지만 실제 호출 가능하다.)
✏️ 특정 빈을 조회할 때 조회된 빈이 2개 이상이면 ?
ac.getBean(DiscountPolicy.class) 으로 DiscountPolicy스프링 빈 조회하려고 한다.
근데 이때 DiscountPolicy의 하위 타입인 FixDiscountPolicy , RateDiscountPolicy둘다 스프링 빈으로 선언되어 있어 ac.getBean(DiscountPolicy.class) 으로 조회하게 되면 반환되는 빈이 2개가 되어 NoUniqueBeanDefinitionException 오류가 발생한다.
NoUniqueBeanDefinitionException: No qualifying bean of type
'hello.core.discount.DiscountPolicy' available: expected single matching bean
but found 2: fixDiscountPolicy,rateDiscountPolicy
이때 하위 타입으로 지정할 수 도 있지만, 하위 타입으로 지정하는 것은 DIP를 위배하고 유연성이 떨어진다. 그리고 이름만 다르고, 완전히 똑같은 타입의 스프링 빈이 2개 있을 때 해결이 안된다.
@Autowired 필드명 매칭
@Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Autowired
private DiscountPolicy rateDiscountPolicy;
- DiscountPolicy 타입으로 매칭을 시도하고 그 결과 여러 개의 빈이 존재할 경우 rateDiscountPolicy 라는 이름을 가진 빈이 있는지 추가로 확인한다.
@Qualifier @Qualifier끼리 매칭 빈 이름 매칭
@Qualifier 는 추가 구분자를 붙여주는 방법이다. 주입시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것은 아니다.
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- @Qualifier 가 mainDiscountPolicy 를 찾은 다음 매칭 한다.
- 만약 @Qualifier 가 mainDiscountPolicy 인 빈이 없다면 빈 이름이 mainDiscountPolicy 빈을 찾아 매칭 시킨다.
- 빈 이름도 동일한 빈이 없다면 NoSuchBeanDefinitionException 예외를 발생한다.
@Primary 사용
@Primary 는 우선순위를 정하는 방법이다. @Autowired 시에 여러 빈이 매칭되면 @Primary 가 우선권을 가진다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
//생성자
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- 우선순위가 더 높은 RateDiscountPolicy를 주입힌다.
✏️ 조회한 빈이 모두 필요할 때 List, Map
의도적으로 정말 해당 타입의 스프링 빈이 다 필요한 경우도 있다.
즉, DiscountPolicy 타입의 모든 빈을 다 조회하고 싶은 경우를 말한다.
이런 경우에는 아래와 같이 List나 Map 자료구조로 모두 조회할 수 있다.
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
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);
}
}
- Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고, 그 값을 DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
- List : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
✏️ 빈 등록 결론
- 편리한 자동 기능을 기본으로 사용하자
- 직접 등록하는 기술 지원 객체는 수동 등록하자
- 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자
'Programming > Spring' 카테고리의 다른 글
[Spring] 빈 스코프 (0) | 2025.03.26 |
---|---|
[Spring] 빈 생명주기 콜백 (0) | 2025.03.26 |
[Spring] 컴포넌트 스캔 & 의존관계 자동 주입 (0) | 2025.03.26 |
[Spring] 싱글톤 컨테이너 (0) | 2025.03.26 |
[Spring] 스프링 컨테이너와 스프링 빈 (0) | 2025.03.26 |