- 앞선 Bean정리 블로그 내용
https://velog.io/@guscjf0903/BEANIoC-의존성
개념이 조금 부족한것 같아서 Bean의 내용을 조금 보충하겠다!
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
}
}
위 코드처럼 AppConfig에서 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들어 사용했었다.
위와같은 AppConfig클래스를 따로 분류하면서 지켜지는 원칙이 있다.
1. SRP 단일 책인 원칙 : 한 클래스는 하나의 책임만 가져야한다.
2. DIP 의존 관계의 원칙 : 추상화에 의존해야지, 구체화에 의존해선 안된다.
3. OCP : 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀있어야한다.
위 3가지 원칙이 AppConfig클래스가 모두 담당하게 됨으로, 개발자는 로직에 조금더 집중할수있고 프로그램 제어 흐름은 AppConfig가 담당하게 된다. 이로써 앞서 정리한 IoC 제어의 역전 (프로그램 제어 흐름을 직접 제어하는것이 아닌 외부에서 관리하게 된다.)이 가능해진다.
ApplicationContext applicationContext = new
AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService =

스프링의 빈은 싱글톤이다! 라는 말로 저번 블로그를 작성하였다 조금 의미를 보충하도록 하겠다
의존관계 주입방법은 다양하게 있다
1. 생성자 주입
2. 수정자 주입(setter 주입)
3. 필드 주입
4. 일반 메서드 주입 (사실상 수정자 주입이랑 똑같다. 그래서 잘 사용되지 않는다)
빈을 사용할려했지만 구현체가 2개이상 만들어져있는 Bean이라면 스프링 NoUniqueBeanDefinitionException 오류가 발생한다.
예시
ac.getBean(DiscountPolicy.class) // 다른 클래스에서 DiscountPolicy빈을 호출
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
// `DiscountPolicy` 의 하위 타입인 `FixDiscountPolicy` , `RateDiscountPolicy` 둘다 스프링 빈으로 선언 되어있음.
그럼 대처하기위해 어떻게 할까?
@Autowired private DiscountPolicy rateDiscountPolicy
위와같이 필드명을 빈이름으로 만들어준다.
@Autowired는 타입을 매칭하고 여러개가 발견되면 필드명을 매칭해서 빈을 찾는다!
2. Qualifier 사용
빈 등록시 @Qualifier를 붙여 준다.
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
주입시에 @Qualifier를 붙여주고 등록한 이름을 적어준다. 생성자 자동 주입 예시
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Primary 는 우선순위를 정하는 방법이다. @Autowired 시에 여러 빈이 매칭되면 @Primary 가 우선권을 가진다. @Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@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;
}
예를 들어서 할인 서비스를 제공하는데, 클라이언트가 할인의 종류(rate, fix)를 선택할 수 있다고 가정해보자. 스프링을 사용하면 소위 말하는 전략 패턴을 매우 간단하게 구현할 수 있다
public class AllBeanTest {
@Test
void findAllBean() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice = discountService.discount(member, 10000, "fixDiscountPolicy");
assertThat(discountService).isInstanceOf(DiscountService.class);
assertThat(discountPrice).isEqualTo(1000);
}
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);
} }
}
DiscountPolicy 를 주입받는다. 이때 fixDiscountPolicy ,rateDiscountPolicy 가 주입된다.: map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로DiscountPolicy` 타입으로 조회한 모든 스프링 빈을 담아준다.출처 및 참고: 김영한의 스프링 핵심원리 기본편 강의