@Configuration을 통한 싱글톤 관리

바람찬허파·2023년 5월 22일
0
post-thumbnail

Spring 컨테이너는 어떻게 객체 인스턴스를 싱글톤으로 관리하는가?

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

해당 예제를 보자, 코드에서는 getBean()을 통해 memberService, orderService, memberRepository를 호출한다.
아래의 AppConfig 코드를 보면 memberService와 orderService는 memberRepository 메소드를 호출한다.

즉, 해당 코드에서는 총 3번 memberRepository()가 호출되어야 한다.

🚨
이를 테스트 해보자.
memberRepository() 메소드 호출시 메시지가 출력되어야 한다.

System.out.println("call AppConfig.memberRepository");

memberRepository()는 3번 호출되지만, 실질적으로 메시지는 단 한 번만 출력된다.

🍀
Spring은 @Configuration을 통해 싱글톤으로 객체 인스턴스를 관리한다.
AppConfig를 상속하는 임의의 클래스를 생성하여 -> 다른 클래스를 빈으로 등록한다.

만약 스프링 빈이 이미 등록되어있다면 -> 기존 빈을 리턴
빈이 등록되어 있지 X -> AppConfig 코드를 통해 빈을 생성, 컨테이너에 등록

해당 로직으로 하나의 객체 인스턴스만 생성되어 싱글톤으로 관리가 가능한 것이다.

+)

만약, @Configuration이 표시되어 있지 않다면?
싱글톤을 보장하지 않는다.

// 싱글톤 테스트 코드 
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);
    //모두 같은 인스턴스를 참고하고 있다.
        System.out.println("memberService -> memberRepository = " +
                memberService.getMemberRepository());
        System.out.println("orderService -> memberRepository  = " +
                orderService.getMemberRepository());
        System.out.println("memberRepository = " + memberRepository);
        //모두 같은 인스턴스를 참고하고 있다.
        assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
    }

    @Test
    void configurationDepp(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        AppConfig bean = ac.getBean(AppConfig.class);
        System.out.println("bean = " + bean.getClass());
    }
}


출력 된 메시지를 보면 모두 같은 스프링 빈을 참조하고 있다.

하지만 @Configuration을 작성하지 않으면, 모두 다른 memberRepository 빈을 참조하고 있다. MemoryMemberRepository@ 뒤의 값이 모두 다르기 때문이다.
다른 객체 인스턴스가 3개나 생성된 것이므로 -> 싱글톤이 지켜지지 않는다는 것을 알 수 있다.

0개의 댓글