ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • @Configuration과 싱글톤
    FrameWork/Spring&Spring-boot 2022. 1. 11. 16:59

    그런데 이상한점이 있다. 다음 AppConfig 코드를 보자

     

    @Configuration
    public class AppConfig {
    
        //@Bean memberService -> new MemoryMemberRepository()
        //@Bean orderService -> new Memory/memberRepository()
    
        @Bean
        public MemberService memberService() {
            return new MemberServiceImpl(memberRepository());
        }
    
        @Bean
        public OrderService orderService() {
            return new OrderServiceImpl(memberRepository(), discountPolicy());
        }
    
    
        @Bean
        public MemoryMemberRepository memberRepository() {
            return new MemoryMemberRepository();
        }
    
    
        @Bean
        public DiscountPolicy discountPolicy(){
            //return new FixDiscountPolicy();
            return new RateDiscountPolicy();
        }
    }

     

     

    @Configuration 안에서 빈이 2번 호출되면 싱글톤 유지가 가능 할까?

     

     

    • memberService빈을 만드는 코드를 보면 'memberRepository'를 호출한다.
      • 이 메서드를 호출하면 'new MemoryMemberRepository( )'를 호출한다.
    • orderService빈을 만드는 코드도 동일하게 'memberRepository( )'를 호출한다.
      • 이 메서드를 호출하면 'new MemoryMemeberRepository( )'를 호출한다.

     

     

    결과적으로 각가 다른  2개의 'MemoryMemberRepository( )'가 생성되면서 싱글톤이 깨지는 것처럼 보인다. 스프링 컨테이너는 이문제를 어떻게 해결할까?

     

     

    import static org.assertj.core.api.Assertions.assertThat;
    
    public class ConfigurationSingletonTest {
    
        @Test
        void configurationTest(){
            ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    
            MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
            OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
            MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
    
    
            MemberRepository memberRepository1 = memberService.getMemberRepository();
            MemberRepository memberRepository2 = orderService.getMemberRepository();
    
    
            System.out.println("memberService -> memberRepository = " + memberRepository1);
            System.out.println("orderService -> memberRepository = " + memberRepository2);
            System.out.println("memberRepository = " + memberRepository);
    
            assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
            assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
        }
    
    }
    

     

    • 확인해보면 memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용된다.
    • AppConfig의 자바 코드를 보면 분명히 각각 2번 'new MemoryMemberRepository' 호출해서 다른 인스턴스가 생성되어야 하는데?
    • 어떻게 된 일일까? 혹시 두번 호출이 안되는 것일 까? 실험을 통해 알아보자

     

    *AppConfig에 호출 로그 남김*

     

    @Configuration
    public class AppConfig {
    
        //@Bean memberService -> new MemoryMemberRepository()
        //@Bean orderService -> new Memory/memberRepository()
    
        @Bean
        public MemberService memberService() {
            System.out.println("call AppConfig.memberService");
            return new MemberServiceImpl(memberRepository());
        }
    
        
        @Bean
        public MemoryMemberRepository memberRepository() {
            System.out.println("call AppConfig.memberRepository");
            return new MemoryMemberRepository();
        }
    
        @Bean
        public OrderService orderService() {
            System.out.println("call AppConfig.orderService");
            return new OrderServiceImpl(memberRepository(), discountPolicy());
        }
    
    
        @Bean
        public DiscountPolicy discountPolicy(){
            //return new FixDiscountPolicy();
            return new RateDiscountPolicy();
        }
    }

     

    결과가 어떻게 출력되야 할까? =>

    memberService()메서드가 호출 될때,먼저 "call AppConfig.memberService"가 호출된다. 그다음에 new를 하면서 memberRepository()메서드가 호출된다. 그 다음에 다음 순서에 있는 memberRepository 메서드가 다시 호출되고,

    그 후엔 다음 순서인 orderService메서드가 호출되고, new 하면서 memberRepository메서드와 discountPolicy가 호출된다.  ......

     

    예상

    //call AppConfig.memberService

    //call AppConfig.memberRepository

    //call AppConfig.memberRepository

    //call AppConfig.orderService

    //call AppConfig.memberRepository

     

     

    -> ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    초기화 될때 Bean 등록할때 스프링 컨테이너가 호출이 된다.

     

    결과

    //call AppConfig.memberService

    //call AppConfig.memberRepository

    //call AppConfig.orderService

     

    memberRepository( )메서드가 세번 호출이 될 것이라고 생각했는데, 실제로는 한번만 호출 되었다.

    => 스프링은 어떤한 방법을 써서라도 싱글톤을 보장해 준다.

    하지만, Java 코드로써는 설명이 안된다.

     

    [출처 : 김영한. 스프링 핵심 원리-기본편. 인프런]

    댓글

Designed by Tistory.