Heestory

05.스프링)싱글톤 컨테이너 본문

개발(~국비)/Spring

05.스프링)싱글톤 컨테이너

까만밀가루 2022. 10. 24. 11:59

 

서론)

스프링이 없는 순수한 DI 컨테이너는 AppConfig에서 요청을 할 때마다 객체를 새로 생성 

→ 고객 그래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸 → 메모리 낭비가 심하다

→ 해당 객체가 딱 1개만 설생되고, 공유하도록 설계한다 → 싱글톤 패턴

 

싱글톤 패턴

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

private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다

이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다.

 

public class SingletonService {

    //1.static 영역에 객체를 딱 1개만 생성한다
    private static final SingletonService instance = new SingletonService();

    //2.public으로 열어서 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 허용
    public static SingletonService getInstance() {
        return instance;
    }

    //3.생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체를 생성하지 못하게 한다.
    private SingletonService(){

    }

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

 

문제점)

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다. getInstance()~ 늘 들어감
  • 의존 관계상 클라이언트가 구체 클래스에 의존
  • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다
  • 테스트하기 어렵다
  • 내부 속성을 변경하거나 초기화 하기 어렵다
  • private 생성자로 자식 클래스를 만들기 어렵다
  • 유연성이 떨어진다

 

싱글톤 컨테이너

스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤(1개만 생성)으로 관리 → 싱글톤 컨테이너 역할, 싱글톤 레지스트리

 

 

싱글톤 방식의 문제점

싱글톤 상태를 무상태(stateless)로 설계해야 한다

public class StatefulService {

//    private int price; //상태를 유지하는 필드

    public int order(String name, int price){
        System.out.println("name = "+name +" price =" +price);
//        this.price = price; //여기가 문제!!
        return price;
    }

//    public int getPrice(){
//        return price;
//    }
}

 

class StatefulServiceTest {

    @Test
    void statefulServiceSingleton(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        StatefulService statefulService1 = ac.getBean(StatefulService.class);
        StatefulService statefulService2 = ac.getBean(StatefulService.class);

        //ThreadA : A 사용자 10000원 주문
        int userAPrice = statefulService1.order("userA",10000);
        //ThreadB : B 사용자 20000원 주문
        int userBPrice = statefulService2.order("userB",20000);

        //ThreadA : 사용자A 주문 금액 조회
//        int price = statefulService1.getPrice();

        System.out.println("price = "+ userAPrice);

//        Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);

    }

    static class TestConfig {

        @Bean
        public StatefulService statefulService() {
            return new StatefulService();
        }
    }

}

 

 

@Configuration과 바이트 코드 조작의 마법

    @Test
    void configurationDeep(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        AppConfig bean = ac.getBean(AppConfig.class);

        System.out.println("bean = "+bean.getClass());
    }

출력값

- 스프링이 CGLIB 라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것 → 싱글톤 보장 되게 도와준다

: @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.

 

만약 @Configuration 없이 @Bean만 적용한다면 @Configuration에 의해 CGLIB 기술이 사용되어 싱글톤이 적용되지만, @Bean만 사용한다면 스프링 빈으로 등록되지만, 승글톤은 보장되지 않는다

 

→ 스프링 설정 정보는 항상 @Configuration 을 사용하자

 


본 내용은 인프런 - 김영한 스프링 기본편 강의 내용 정리용으로 작성하였습니다

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

 

'개발(~국비) > Spring' 카테고리의 다른 글

07.스프링)의존관계 자동 주입  (0) 2022.10.24
06.스프링) 컴포넌트 스캔  (0) 2022.10.24
04.스프링)스프링 컨테이너와 스프링 빈  (0) 2022.10.22
02~03.스프링)핵심 원리 이해  (0) 2022.10.22
01.스프링  (0) 2022.10.20