Blog

[SpringCore] @Qulifier 추가 탐색, @Primary 우선권 지정, 애너테이션을 직접 만들기

Category
Author
citeFred
citeFred
Tags
PinOnMain
1 more property
같은 타입의 빈을 구분하기위한 @Qualifier 어노테이션
Table of Content

구현체가 2개인 상황

DiscountPolicy는 고정 할인인 FixDiscountPolicy, 비율 할인인 RateDiscountPolicy 처럼 구현체가 2개가 있으며 계획에 따라 선택하여 사용하고 있다.
package hello.core.discount; import hello.core.member.Grade; import hello.core.member.Member; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class FixDiscountPolicy implements DiscountPolicy { private int discountFixAmout = 1000; // 1000원 기본 할인 @Override public int discount(Member member, int price) { if (member.getGrade() == Grade.VIP) { return discountFixAmout; } else { return 0; } } }
Java
복사
package hello.core.discount; import hello.core.member.Grade; import hello.core.member.Member; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class RateDiscountPolicy implements DiscountPolicy { private int discountPercent = 10; @Override public int discount(Member member, int price) { if (member.getGrade() == Grade.VIP) { return price * discountPercent / 100; } else { return 0; } } }
Java
복사
하지만 이처럼 두 클래스 모두 @Component를 명시하게 되면 @ComponentScan에 의해 모두 스프링 빈으로 등록되면서 추상 클래스 DiscountPolicy를 호출하는 단계에서 스프링은 어떤 것을 선택 할 지 알 수 없게 된다.
여기서 서비스 코드에 직접 Fix 또는 Rate를 명시하면 DIP를 위반하면서 최상위 부모 또는 추상화를 주입해야되는 설계 원칙을 위반하게 된다.

@Qualifier 어노테이션으로 구분하기

각 구현체 클래스에 어노테이션으로 명시적으로 이름을 구분 할 수 있다.
@Component @Qualifier("fixDiscountPolicy") public class FixDiscountPolicy implements DiscountPolicy { private int discountFixAmout = 1000; // 1000원 기본 할인 ...
Java
복사
@Component @Qualifier("mainDiscountPolicy") public class RateDiscountPolicy implements DiscountPolicy { private int discountPercent = 10; ...
Java
복사
다음처럼 어노테이션으로 이름을 지정해준다. 구현체를 호출하는 부분에서도 @Qualifier 를 통해 호출하고자 하는 빈을 특정하게 된다.
@Component public class OrderServiceImpl implements OrderService{ private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; @Autowired public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
Java
복사
이러면 어노테이션으로 mainDiscountPolicy 이름으로 등록된 빈을 추가로 스캔하면서 RateDiscountPolicy 객체를 지정하게 된다.
이처럼 명확하게 특정 빈을 찾는 용도로만 사용하는 것이 좋다.

@Primary로 여러 구현체에서 기 본값

DiscountPolicy 구현체 중에서 RateDiscountPolicy가 선택되도록 하려면 @Primary 어노테이션을 작성하면 된다.
@Component @Primary public class RateDiscountPolicy implements DiscountPolicy { private int discountPercent = 10; ...
Java
복사
@Component public class FixDiscountPolicy implements DiscountPolicy { private int discountFixAmout = 1000; // 1000원 기본 할인 ...
Java
복사

우선권

이처럼 @Primary는 마치 default 처럼 등록하는 개념이며 @Qualifier는 커스텀하여 지정하는 개념이다.
따라서 @Qualifier가 더 상세하므로 우선순위가 높다 > @Primary

애너테이션 만들기

@Qualifier로 특정 이름을 구분하는 것은 컴파일 단계에서의 문자열 오기입을 찾을 수 없다.(ex: mainnDiscounttPolicy 처럼 잘못 기입하는 사례 등)
따라서 어노테이션을 직접 만들어서 사용하는 방법이 있다.
우선 MainDiscountPolicy라는 인터페이스를 만들어 다음과 같이 작성하여 애너테이션임을 작성
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Qualifier("mainDiscountPolicy") public @interface MainDiscountPolicy { }
Java
복사
해당 애너테이션이 적용될 구현체 클래스에 애너테이션을 추가 작성한다.
@Component @MainDiscountPolicy public class RateDiscountPolicy implements DiscountPolicy { private int discountPercent = 10; @Override public int discount(Member member, int price) { if (member.getGrade() == Grade.VIP) { return price * discountPercent / 100; } else { return 0; } } }
Java
복사
이제 RateDiscountPolicy@MainDiscountPolicy 애너테이션으로 적용되었으며 해당 구현체를 호출하는 곳에서도 동일한 애너테이션을 작성해서 호출하여 특정 구현체를 명시적으로 지목하게 된다.
@Component public class OrderServiceImpl implements OrderService{ private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; @Autowired public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; } ...
Java
복사
Search
 | Main Page | Category |  Tags | About Me | Contact | Portfolio