의존관계 주입 크게 4가지 : 생성자, 수정자(setter), 필드, 일반 메서드
@Autowired 생략 가능@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
@Autowired 기본 동작 -> 주입 대상 없으면 error. 주입 대상 없어도 동작하려면 @Autowired(required = false)@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
주입할 빈 없어도 동작해야 할 때가 있음. 아까 알아보았듯, @Autowired(required=false) 처럼 옵션 처리하는 여러 방법들 존재.
@Autowired(required=false) : 자동 주입 대상 없으면 수정자 메서드 자체가 호출 안됨.@Nullable : 자동 주입 대상 없으면 null 입력됨//호출 안됨
@Autowired(required = false)
public void setNoBean1(Member member) {
System.out.println("setNoBean1 = " + member);
}
//null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
System.out.println("setNoBean2 = " + member);
}
//Optional.empty 호출
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
System.out.println("setNoBean3 = " + member);
@@강의 확인
set으로 하면,
final 사용 가능 이점 : 생성자에서 값 설정 안된 오류를 컴파일 시점에서 막아줌.
참고 : 수정자 주입 포함한 나머지 방식은 생성자 이후 호출이므로, final 키워드 사용 불가능.
오직 생성자 주입만 가능.
생성자 만들기, 주입 받은 값 대입, -- -> 간편한 방법 찾기 -> Lombok
@RequiredArgsConstructor -> final 붙은 필드 모은 후 생성자 자동 생성.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired
private DiscountPolicy discountPolicy
-> NoUniqueBeanDefinitionException 오류 발생
@Autowired
1.타입 매칭
2.타입 매칭 결과 2개 이상일 시 필드 명, 파라미터 명으로 빈 이름 매칭
@Autowired
private DiscountPolicy discountPolicy //Rate, Fix 총 두 개라 못 정함.
@Autowired
private DiscountPolicy rateDiscountPolicy //필드명으로 확정지어줌.
필드 명 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;
}
수정자 자동 주입 예시
@Autowired
public DiscountPolicy setDiscountPolicy(@Qualifier("mainDiscountPolicy")
DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
@Qualifier("mainDiscountPolicy") 못찾으면 -> mainDiscounPolicy 이름의 스프링 빈을 찾으러 감.
이렇게 빈까지 뒤져가며 찾아주지만, @qualifier는 @qualifier 찾는 용도로만 사용하는게 명확.
@Qualifier 정리
1. @Qualifier끼리 매칭
2. 빈 이름 매칭
3. NoSuchBeanDefinitionException 예외 발생
우선순위를 정하는 방법. @Autowiried에 여러 빈 나올 시, @Primary 붙은게 우선권 가짐.
사용 예시
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Qualifier 단점은 주입받을 때 모든 코드에 붙여줘야함. 반면 @Primary는 그럴 필요 X.
@Primary, @Qualifier 활용
메인으로 쓰이는 빈은 @Primary로 @Qualifier 없이 편리하게 조회,
서브로 쓰이는 빈은 @Qualifier로 명시적으로 획득하는 방식으로 사용.
스프링은 자동보다는 수동이, 넓은 범위 선택권 보단 좁은 범위 선택권이 우선 순위 높음. 그러므로 여기에서도 @Qualifier가 우선권 높음.
@Qualifier("mainDiscountPolicy") 처럼 문자를 적으면 컴파일 때 체크 안됨(오타 나도 문제 모름)-> 컴파일러가 안잡아줘서 실행 중 문제 갈생 가능.
-> 어노테이션 만들어서 해결 가능.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {}
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
List, Map으로 받은 후, key값을 이용하여 변수에 저장.
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);
}
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);
}
}