생성자 주입을 사용하면 주입 데이터를 누락 했을 때 컴파일 오류가 발생한다. 필요로 하는 타입을 바로 알 수 있기때문에 누락되는 경우가 없다.
java: constructor OrderServiceImpl in class dh0023.springcore.order.service.OrderServiceImpl cannot be applied to given types;
required:dh0023.springcore.member.repository.MemberRepository,dh0023.springcore.discount.service.DiscountPolicy found: no arguments reason: actual and formal argument lists differ in length
bean으로 등록되지 않은 Member 클래스를 @Autowired를 하면 다음과 같은 오류가 발생한다.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'autowiredTest.TestBean': Unsatisfied dependency expressed through method 'setNoBean' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'dh0023.springcore.member.domain.Member' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:768)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:720)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413)
....
다음과 같이 @Autowired(required = false)로 자동주입 대상을 주입할 수 있다.
23:27:46.560 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@223aa2f7
23:27:46.591 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
23:27:46.721 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
23:27:46.730 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
23:27:46.733 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
23:27:46.736 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
23:27:46.757 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autowiredTest.TestBean'
member = Optional.empty
Process finished with exit code 0
조회 빈이 2개 이상인 경우
기존에는 RateDiscountPolicy 만 @Component로 등록을 했었는데, FixDiscountPolicy까지 @Component로 등록하게 되면 NoUniqueBeanDefinitionException 오류가 발생한다.
@ComponentpublicclassOrderServiceImplimplementsOrderService{// final은 반드시 값이 있어야한다.privatefinalMemberRepository memberRepository;privatefinalDiscountPolicy discountPolicy;// 생성자 의존관계 주입 @AutowiredpublicOrderServiceImpl(MemberRepository memberRepository,DiscountPolicy discountPolicy) {this.memberRepository= memberRepository;this.discountPolicy= discountPolicy; }}
Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'dh0023.springcore.discount.service.DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy
이때 하위 타입을 지정할 수 있지만, 이는 DIP를 위배하고 유연성이 떨어진다. 이름만 다르고 완전히 똑같은 타입의 스프링빈이 여러개(상속, 구현)인 경우 해결이 안된다.
이때, 자동 의존 주입으로 해결할 수 있는 방법이 약 3가지 정도 있다.
@Autowired 필드명
@Autowired 는 최초에 타입 매칭을 시도하는데, 이때 빈이 여러개라면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
생성자 주입
AS-IS
@ComponentpublicclassOrderServiceImplimplementsOrderService{// final은 반드시 값이 있어야한다.privatefinalMemberRepository memberRepository;privatefinalDiscountPolicy discountPolicy;// 생성자 의존관계 주입 @AutowiredpublicOrderServiceImpl(MemberRepository memberRepository,DiscountPolicy discountPolicy) {this.memberRepository= memberRepository;this.discountPolicy= discountPolicy; }}
TO-BE
@ComponentpublicclassOrderServiceImplimplementsOrderService{// final은 반드시 값이 있어야한다.privatefinalMemberRepository memberRepository;privatefinalDiscountPolicy discountPolicy;// 생성자 의존관계 주입 @AutowiredpublicOrderServiceImpl(MemberRepository memberRepository,DiscountPolicy rateDiscountPolicy) {this.memberRepository= memberRepository;this.discountPolicy= rateDiscountPolicy; }}
/** * @Component 어노테이션 추가로 빈설정 * 이때 빈이름을 설정하고 싶은 경우에는 @Component("빈이름")으로 설정할 수 있다. * @Qualifier : 추가 구분자 설정 */@Component@Qualifier("rateDiscountPolicy")publicclassRateDiscountPolicyimplementsDiscountPolicy{privatefinalstaticint DIS_PER =10; @Overridepublicintdiscount(Member member,int price) {if (member.getGrade() ==Grade.VIP){return price * DIS_PER /100; } else {return0; } }}