이 글은 인프런에서 김영한 님의 "스프링 핵심원리 - 기본편"을 수강 후 공부한 내용을 정리한 게시글입니다. 부족한 부분이 있다면 언제든 지적 부탁드립니다.
다양한 의존 관계 주입 방법에 대해서는 “이전 게시물”을 확인해주세요.
이전 게시물 - 의존관계 자동주입 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
생성자 주입을 작성하는 과정에서 비슷한 코드르 여러번 작성하게 된다.
다 형식이 정해져 있는 코드들이기때문에 이를 롬북이라는 라이브러리에서 코드 작성을 간소화 시켜준다.
@RequiredArgsConstructor
기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다. (다음 코드에는 보이지 않지만 실제 호출 가능하다.)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
private DiscountPolicy rateDiscountPolicy;
@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;
}
NoSuchBeanDefinitionException
예외를 발생한다.@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;
}
의도적으로 정말 해당 타입의 스프링 빈이 다 필요한 경우도 있다.
즉, 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);
}
}