
public class SingletonTest {
    @Test
    @DisplayName("스프링 없는 순수한 DI 컨테이너")
    void pureContainer(){
        AppConfig appConfig = new AppConfig();
//      1. 조회 : 호출할 때마다 객체를 생성
        MemberService memberService1 = appConfig.memberService();
//      2. 조회 : 호출할 때마다 객체를 생성
        MemberService memberService2 = appConfig.memberService();
//      3. 참조값이 다른 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);
//      memberService1과 memberService2가 다른지 자동화로 만들어보자
        assertThat(memberService1).isNotSameAs(memberService2);
    }
}

결과를 보면 memberService1과 memberService2는 서로 다른 객체이다. 즉, 다른 객체가 생성되어 JVM에 각각 올라가게 된다.
public class SingletonService {
    // 자바의 static 영역 공부해보세요!
    private static final SingletonService instance = new SingletonService();
    public static SingletonService getInstance() {
        return instance;
    }
    // 생성자를 private으로 설정해서 밖에서 못 만들게 설정
    private SingletonService() {
    }
    public void logic() {
        System.out.println("싱글톤 객체 로직 호출");
    }
}
static 영역에 객체 instance를 미리 하나 생성해서 올려둔다.getInstance() 메서드를 통해서만 조회할 수 있다.new키워드로 객체 인스턴스가 생성되는 것을 막는다. @Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    void SingletonServiceTest(){
        SingletonService singletonService1 = SingletonService.getInstance();
        SingletonService singletonService2 = SingletonService.getInstance();
        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);
        assertThat(singletonService1).isSameAs(singletonService2);
        // same (==)
        // equal (Java의 eqauls 메서드)
    }

싱글톤 패턴을 적용하면 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다.
하지만 싱글톤 패턴은 다음과 같은 수 많은 문제점을 가지고 있다.
구체클래스.getInstance()private 생성자로 자식 클래스를 만들기 어렵다.💡 결론적으로 유연성이 떨어진다.
💡 안티패턴으로 불리기도 한다.
위 싱글톤 패턴의 문제점을 제거하고, 객체를 싱글톤으로 관리해주는 싱글톤 컨테이너에 대해 알아보자!