Spring 기본편

@Configuration과 싱글톤

J-Chris 2023. 3. 22. 06:45

사실 @Configuration은 싱글톤 때문에 있는 것이다.

다음 AppConfig 코드를 보자. (이상한 것이 있다)

 

 

public class AppConfig {

    // @Bean memberService --> new MemoryMemberRepository()
    // @Bean orderService --> new MemoryMemberRepository()
    

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(new memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }


    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(
                memberRepository(),
                discountPolicy());
    }
    
    ...
    
 }

- memberService 빈을 만드는 코드를 보면 memberRepository()를 호출한다.

    - 이 메서드를 호출하면 new MemoryMemberRepository()를 호출한다.

- orderService 빈을 만드는 코드도 동일하게 memberRepository()를 호출한다.

    - 이 메서드를 호출하면 new MemoryMemberRepository()를 호출한다.

 

결과적으로 각각 다른 2개의 MemoryMemberRepository가 생성되면서 싱글톤이 꺠지는 것처럼 보인다.

스프링 컨테이너는 이 문제를 어떻게 해결할까 ?

 

직접 테스트 해보자.

 

`검증 용도의 코드 추가`

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를 호출해서 다른 인스턴스가 생성되어야 하는데?
  • 어떻게 된 일일까 ? 혹시 두번 호출이 안되는 것일까 ? 

스프링 컨테이너가 @Bean을 호출해서 스프링 빈을 생성한다. 그래서 memberRepository()는 다음과 같이

총 3번이 호출되어야 하는 것 아닐까?

  • 1. 스프링 컨테이너가 스프링 빈에 등록하기 위해 @Bean이 붙어있는 memberRepository() 호출
  • 2. memberService() 로직에서 memberRepository() 호출
  • 3. orderService() 로직에서 memberRepository() 호출

그런데 출력 결과는 모두 1번만 호출된다.

call AppConfig.memberSerivce

call AppConfig.memberRepository

call AppConfig.orderService