ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring][Kotlin] 싱글톤 컨테이너
    Spring 2022. 5. 18. 19:19

     

    스프링은 기업용 온라인 서비스 기술을 지원하기 위해 탄생했다.
    그리고 대부분 스프링 애플리케이션은 웹 애플리케이션이다.
    웹앱은 보통 여러 고객이 동시에 요청한다.

    Problem] Request 마다 객체 생성하면 메모리 낭비가 매우 심하다.
    Sol) 해당 객체를 1개만 생성하고  공유하도록 하면 된다.
    (사실 요즘 GC가 좋아서 초당 100최 정도는 괜찮지만 효율적이면 좋으니까)

     

    싱글톤 패턴

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

     -> 객체 인스턴스가 2개 이상 생성 못하도록 막야야 한다.

     

    싱글톤 구현 방법은 많다.
    그 중 하나의 방법은 private 생성자를 사용해서 생성자를 사용하지 못하도록 막는 것이다.
    Kotlin에서는 object 키워드를 붙이면 싱글톤 클래스 생성이 가능하다.
    ※단, 생성자에 파라미터 넣지 않는 클래스에서만 사용이 가능하다.
    생성자 통해 파라미터 전달받는 싱글톤 클래스는 companion object를 사용하면 된다.

     

    싱글톤 패턴의 문제점

    • 구현 코드 자체가 많이 들어간다.
    • 클라이언트가 구체 클래스에 의존한다.(내부적으로 객체 반환 함수를 만든다면) -> DIP위반, OCP위반 가능성↑
    • 유연성이 떨어져서 테스트 하기가 어렵다.
    • 내부 속성을 변경하고 초기화 하기 어렵다.
    • private생성자로 자식 클래스를 만들기 어렵다.
    • 안티패턴으로 불리기도 한다.

     -> 스프링 컨테이너는 default로 객체를 싱글톤으로 만들어서 관리해준다(성능 굿굿)

     

    싱글톤 컨테이너

     : 싱글톤 패턴의 문제점을 해결하면서 객체 인스턴스를 싱글톤으로 관리해준다.

    (객체를 미리 생성해서 관리해주기 때문에 지저분한 코드가 없고 DIP, OCP, 테스트 모두 유연하게 가능하다.)

    실제로 조회할 때 마다 같은 객체를 반환한다.

     

    싱글톤 방식의 주의점

     : 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 stateful 하게 설계하면 안된다.

     

    무상태 stateless

    특정 클라이언트에 의존적인 필드가 있으면 안된다.

    특정 클라이언트가 값 변경할 수 있는 필드가 있으면 안된다

    가급적 읽기만 가능하게 해야한다.

    필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 이용해야한다.

    ※스프링 빈의 필드에 공유 값 설정 시 큰 장애가 발생한다.

    => 스프링은 항상 무상태로 설계하자!

     

    @Configuration과 싱글톤

    @Configuration은 싱글톤을 위해 존재한다.

    @Bean
    fun memberService() = MemberServiceImpl(memberRepository())
    
    @Bean
    fun orderService() = OrderServiceImpl(memberRepository(), discountPolicy())
    
    @Bean
    fun memberRepository() = MemoryMemberRepository()

    위 예제코드의 경우 memberService와 orderService 빈을 만드는 코드를 보면 memberRepository()를 호출한다.

    또 memberRepository 빈을 만드는 코드도 실행된다.

    그런데 memberRepository는 MemoryMemberRepository 생성자를 호출하며 3개의 객체가 생성되면서 싱글톤이 깨지는 것 처럼 보인다.

    하지만 memberRepository는 빈 등록 시 1번만 호출된다. -> 싱글톤 보장

     

    @Configuration과 바이트 코드 조작

    위의 Kotlin코드 자체만 보면 3번 호출되어야하는게 맞지만, 

    스프링은 이것이 되게 하기 위해 바이트코드를 조작하는 라이브러리를 사용한다.

    @Test
    @DisplayName("CGLIB 확인하기")
    fun confiruationDeep(){
        val bean = ac.getBean(AppConfig::class.java)
        println("bean = ${bean::class.java}")
    }

    예제 코드 실행 시 나온 결과

    클래스 정보를 출력하면 클래스 이름인 AppConfig가 아닌 AppConfig&&EnhancerBySpringCGLIB$$~~~가 출력된다.

     

    CGLIB

    이것은 사용자가 만든 클래스가 아니라 스프링이 CGLIB라는 바이트 조작 라이브러리를 사용해서 해당 클래스를 상속받은 임의의 다른 클래스를 만들고 그 클리스를 스프링 빈으로 등록한 것이다.

    -> 물론 해당 클래스의 자식 타입이므로 스프링 빈의 해당 클래스 타입으로 조회가 가능하다.

     

    만약 @Configuration를 안붙이면 CGLIB 안쓰기 때문에 순수한 인스턴스가 조회되고 싱글톤이 깨진다.

     

     

     

    [출처]

    스프링 핵심 원리 - 기본편

    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

     

    스프링 핵심 원리 - 기본편 - 인프런 | 강의

    스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

    www.inflearn.com

     

Designed by Tistory.