본문 바로가기
Spring/Spring core basic

[Spring core basic] 38 - 주입 받은 빈이 모두 필요할 때

by Kloong 2022. 5. 3.

참고

더보기

Spring core basic 시리즈는 김영한 님의 "스프링 핵심 원리 - 기본편" 강의를 정리한 글입니다. 글에 첨부된 사진은 해당 강의의 강의 자료에서 캡쳐한 것입니다. 제 Github에만 올려뒀다가, 정보 공유와 강의 홍보(?)를 위해 블로그에도 업로드합니다. 마크다운을 잘 쓰지 못해서 가독성이 조금 떨어지는 점 양해 바랍니다.

 

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

주입 받은 빈이 모두 필요할 때

의존관계 주입을 위해 특정 타입의 빈을 조회할 때, 같은 타입의 빈이 여러개 존재할 수 있다. 그런데 설계상 해당 타입의 스프링 빈이 전부 필요한 경우도 있다.

예를 들어서 할인 서비스를 제공하는데, 클라이언트가 할인의 종류(rate, fix)를 선택할 수 있다고 가정해보자. 스프링을 사용하면 이와 같은 소위 "전략 패턴"을 매우 간단하게 구현할 수 있다.

AllBeansTest.java

//package, import 생략

public class AllBeansTest {
    @Test
    void findAllBean() {
        ApplicationContext ac =
        new AnnotationConfigApplicationContext(
        AutoAppConfig.class, DiscountService.class);
    }

    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);
        }
    }
}

스프링 컨테이너를 생성할 때, AutoAppConfig.classDiscountService.class 를 넘겨주면, 두 클래스의 내용을 가지고 빈을 등록한다. 가변길이 매개변수를 받는 생성자를 활용한 것이다.

만약 AutoAppConfig.class를 안 넘겨준 뒤 실행해보면, DiscountPolicy에 해당하는 빈이 존재하지 않기 때문에 policyMap과 policies는 비어있는 map과 list가 된다.

해당하는 빈이 없지만 예외가 나오지 않는 이유는 해당 타입의 빈을 전부 조회해서 map과 list로 받아오는 형태이기 때문에, 빈이 없어도 굳이 예외처리하지 않고 그냥 비어있는 map과 list를 넘겨주면 되기 때문인 듯.

출력을 살펴보면,

policyMap = {fixDiscountPolicy=com.kloong.corebasic1.discount.FixDiscountPolicy@291f18, rateDiscountPolicy=com.kloong.corebasic1.discount.RateDiscountPolicy@17d88132}
policies = [com.kloong.corebasic1.discount.FixDiscountPolicy@291f18, com.kloong.corebasic1.discount.RateDiscountPolicy@17d88132]

fixDiscountPolicy와 rateDiscountPolicy 두 개의 빈이 map과 list에 들어있는 것을 확인할 수 있다. 싱글톤이므로 객체는 동일하다.

map에서는 빈 이름이 key, 빈의 주소가 value임을 볼 수 있다.

이를 활용하여 할인 서비스의 로직을 개발해보자.

AllBeansTest.java

//package, import 생략

public class AllBeansTest {
    @Test
    void findAllBean() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);

        DiscountService discountService = ac.getBean(DiscountService.class);

        //고정 할인을 선택한 멤버
        Member member1 = new Member(1L, "user1", Grade.VIP);
        int discountPrice1 =
        discountService.discount(member1, 10000, "fixDiscountPolicy");

        //비율 할인을 선택한 멤버
        Member member2 = new Member(1L, "user2", Grade.VIP);
        int discountPrice2 =
        discountService.discount(member2, 20000, "rateDiscountPolicy");

        Assertions.assertThat(discountService).isInstanceOf(DiscountService.class);
        Assertions.assertThat(discountPrice1).isEqualTo(1000);
        Assertions.assertThat(discountPrice2).isEqualTo(2000);
    }

    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);
        }

        //map에 key로 저장되어 있는 빈 이름을 활용하여
        //멤버가 선택한 할인 방식으로 할인을 해준다.
        public int discount(Member member, int price, String discountCode) {
            DiscountPolicy discountPolicy = policyMap.get(discountCode);
            return discountPolicy.discount(member, price);
        }
    }
}
의존관계 주입 분석
  • Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
  • List<DiscountPolicy> : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
  • 만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 map을 주입한다.
로직 분석
  • DiscountService는 Map으로 모든 DiscountPolicy 를 주입받는다. 이때 fixDiscountPolicy , rateDiscountPolicy 빈이 주입된다.
  • discount () 메서드는 discountCode로 "fixDiscountPolicy"가 넘어오면 map에서fixDiscountPolicy 스프링 빈을 찾아서 실행한다. “rateDiscountPolicy”가 넘어오면rateDiscountPolicy 스프링 빈을 찾아서 실행한다.

참고 - 스프링 컨테이너를 생성하면서 스프링 빈 등록하기

스프링 컨테이너는 생성자에 클래스 정보를 받는다. 여기에 클래스 정보를 넘기면 해당 클래스가 스프링 빈으로 자동 등록된다.

new AnnotationConfigApplicationContext(AutoAppConfig.class,DiscountService.class);

이 코드는 2가지로 나누어 이해할 수 있다.

  • new AnnotationConfigApplicationContext()를 통해 스프링 컨테이너를 생성한다.
  • AutoAppConfig.class , DiscountService.class 를 파라미터로 넘기면서 해당 클래스를 자동으로스프링 빈으로 등록한다.

정리하면 스프링 컨테이너를 생성하면서, 해당 컨테이너에 동시에 AutoAppConfig , DiscountService를 스프링 빈으로 자동 등록한다.

댓글