Study/Spring

[스프링 핵심 원리 - 기본편] 싱글톤 컨테이너(@Configuration과 싱글톤)

black6765 2023. 1. 1. 22:38

테스트 코드 추가

package hello.core.singleton;

import hello.core.AppConfig;
import hello.core.member.MemberRepository;
import hello.core.member.MemberServiceImpl;
import hello.core.order.OrderServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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 -> memberRepository1 = " + memberRepository1);
        System.out.println("orderService -> memberRepository2 = " + memberRepository2);
        System.out.println("memberRepository = " + memberRepository);

        Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
    }
}
  • 실행 결과 세 개의 객체는 동일함
  • 확인해보면 memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용

 

AppConfig 수정(디버그)

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository 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 빈을 만드는 코드를 보면 memberRepository() 를 호출한다.
    • 이 메서드를 호출하면 new MemoryMemberRepository() 를 호출한다.
  • orderService 빈을 만드는 코드도 동일하게 memberRepository() 를 호출한다.
    • 이 메서드를 호출하면 new MemoryMemberRepository() 를 호출한다.
  • AppConfig의 자바 코드를 보면 각각 2번 new MemoryMemberRepository를 호출해서 다른 인스턴스가 생성되는 것으로 보인다
    • 결과적으로 각각 다른 2개의 MemoryMemberRepository가 생성되면서 싱글톤이 깨지는 것 처럼 보임
  • 디버그를 위해 각 메소드 호출 시 출력문 추가

 

테스트 실행 결과

// 출력 결과
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
  • memberRepository() 메소드가 단 한 번만 실행됨
  • 스프링 컨테이너가 각각 @Bean을 호출해서 스프링 빈을 생성한다. 그래서 memberRepository() 는 다음과 같이 총 3번이 호출되어야 하는 것으로 보인다.
    • 스프링 컨테이너가 스프링 빈에 등록하기 위해 @Bean이 붙어있는 memberRepository() 호출
    • memberService() 로직에서 memberRepository() 호출
    • orderService() 로직에서 memberRepository() 호출
  • 그런데 출력 결과는 모두 1번만 호출된다.
  • 어떻게 이런 결과가 나온 것인지는 다음 강의에서 알아본다.

 

※ 본 게시글은 인프런의 스프링 핵심 원리 - 기본편(김영한)을 수강하고 정리한 내용입니다.