Spring/스프링 핵심 원리 - 기본편

[싱글톤 컨테이너] 싱글톤 패턴의 문제를 해결하는 스프링 컨테이너

alsruds 2023. 11. 29. 21:15
🧐강의🧐
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

💡 스프링 없는 순수 DI 컨테이너 (싱글톤 X)

동시 요청

 

TEST

☑️ 매 호출 시, 다른 객체가 생성됨을 확인하기

public class SingletonTest {

    @Test
    @DisplayName("스프링 없는 순수한 DI 컨테이너")
    void pureContainer() {
        AppConfig appConfig = new AppConfig();

        // 1. 조회 : 호출할 때마다 객체 생성
        MemberService memberService1 = appConfig.memberService();

        // 2. 조회 : 호출할 때마다 객체 생성
        MemberService memberService2 = appConfig.memberService();

        // 참조값이 다른 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        // memberService1 ≠ memberService2
        Assertions.assertThat(memberService1).isNotSameAs(memberService2);
    }
}

참조값이 다른 것을 확인할 수 있다

 

AppConfig (스프링 없는 순수 DI 컨테이너) : 요청할 때마다 새로운 객체 생성 → 메모리 낭비

싱글톤 패턴 : 해당 객체를 1개만 생성한 후 공유

 

💡 싱글톤 패턴

· 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴

· private 생성자 사용 : 외부에서 임의로 new 키워드 사용하는 것 방지 → 객체 인스턴스 2개 이상 생성 방지

 

TEST

☑️ 매 호출 시, 같은 객체가 생성됨을 확인하기

// SingletonService
public class SingletonService {
    private static final SingletonService instance = new SingletonService();

    // 호출할 때마다 같은 객체 인스턴스 반환
    public static SingletonService getInstance() {
        return instance;
    }
    
    // private 으로 new 키워드 생성 방지    
    private SingletonService() {}

    public void logic() {
        System.out.println("싱글톤 객체 로직 호출");
    }
}

// SingletonTest
public class SingletonTest {

    ...

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

같은 객체가 생성된 것을 확인할 수 있다

 

싱글톤 패턴 문제점
구현 코드의 양이 많음
클라이언트가 구체 클래스에 의존 → DIP, OCP 위반
테스트가 어려움
내부 속성 변경 or 초기화 어려움
private 생성자 → 자식 클래스 만들기 어려움
유연성이 떨어짐
안티패턴

 

💡 싱글톤 컨테이너

이미 만들어진 객체 공유 : 효율적인 재사용

 

· 스프링 컨테이너 : 싱글톤 컨테이너의 역할

          》싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤(1개만 생성)으로 관리

          》싱글톤 패턴 단점 해결 : 지저분한 코드 X, DIP/OCP/테스트/private 생성자로부터 자유롭게 싱글톤 사용

          》싱글톤 레지스트리 : 싱글톤 객체를 생성하고 관리하는 기능

 

TEST

☑️ 매 호출 시, 같은 객체가 생성됨을 확인하기

public class SingletonTest {

    ...

    @Test
    @DisplayName("스프링 컨테이너와 싱글톤")
    void springContainer() {
        // AppConfig appConfig = new AppConfig();
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        // 1. 조회 : 호출할 때마다 객체 생성
        MemberService memberService1 = ac.getBean("memberService", MemberService.class);

        // 2. 조회 : 호출할 때마다 객체 생성
        MemberService memberService2 = ac.getBean("memberService", MemberService.class);

        // 참조값이 같은 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        // memberService1 = memberService2
        assertThat(memberService1).isSameAs(memberService2);
    }
}

같은 객체 출력