[Spring Study] - Singleton

uHan2·2021년 1월 5일
0

TIL.BackEnd

목록 보기
6/12

비록 시작은 코딩일기지만, 그 끝은 창대하게
어엿한 개발자 블로그로 성장할 수 있도록.


Spring Study

본 게시글은
Endless Creation Spring Study
에 사용하는 자료입니다.


관심사의 분리

  • 객체를 생성하고 연결하는 역할실행하는 역할이 분리
  • 조립 (객체 생성과 의존관계 설정)은 누군가가 해주고 나는 실행하는 로직만 수행
  • 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중
  • 사용 영역 과, 객체를 생성하고 구성(Configuration)하는 구성 영역 으로 분리
@Configuration
public class AppConfig
{
    @Bean
    public MemberRepository memberRepository()
    {
        return new MemoryMemberRepository();
    }

    @Bean
    public DiscountPolicy discountPolicy()
    {
        return new RateDiscountPolicy();
    }

    @Bean
    public MemberService memberService()
    {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService()
    {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
}

여기서 @Configuration, @Bean Annotation을 지우면
순수 자바 코드로 작성된 DI 컨테이너 이다.
교재에서 나온 Assembler.class 와 같음 (오브젝트 팩토리 라고 불리기도 함)

다음과 같이 ApplicationContext 에 우리가 작성한 Config.class 를 넘겨주면
Spring 이 우리가 설정한 정보를 참고해서 구성하는 역할을 맡게된다.

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Assembler.class);

우리가 직접 구성하지 않고 Spring 에 맡기는 이유 / 장점은 여러가지 있지만
Singleton 이 대표적이다.


Singleton

Singleton PatternInstance오직 1개만 생성되야 하는 경우에 사용되는 패턴이다.
예를 들어 레지스트리 같은 설정 파일의 경우 객체가 여러개 생성되면 설정 값이 변경될 위험이 생길 수 있다.

Instance 가 1개만 생성되는 특징을 가진 Singleton Pattern 을 이용하면, 하나의 Instance를 메모리에 등록해서 여러 쓰레드가 동시에 해당 Instance공유하여 사용하게끔 할 수 있으므로, 요청이 많은 곳에서 사용하면 효율을 높일 수 있다.

단, Singleton을 적용할때 Concurrency Problem 을 고려해야 한다.

  • Singleton 적용

  • Singleton 적용


Singleton :: Demerit

Java의 Singleton Pattern 에는 다음과 같은 한계점이 있다.

  • Singleton Pattern 을 구현하는 코드 자체가 많이 들어간다.
  • 의존관계상 클라이언트가 구체 클래스에 의존한다. (DIP 위반)
  • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
  • 테스트하기 어렵다.
  • 내부 속성을 변경하거나 초기화 하기 어렵다.
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 유연성이 떨어진다.
  • 안티패턴으로 불리기도 한다.

그럼 Java 의 Singleton Pattern과 Spring 의 Singleton의 차이는?

  • Spring ContainerSingleton Pattern 의 문제점을 해결하면서, 객체 인스턴스를 싱글톤(1개만 생성)으로 관리한다.

  • Java의 Singleton은 Class Loader에 의해 구현되고,
    Spring의 Singleton은 Spring Container 에 의해 구현된다.

  • Java의 Singleton Scope는 코드 전체이고,
    Spring의 Singleton Scope는 해당 컨테이너 내부이다.

  • Java로 구현하는 Singleton은 개발자의 로직에 따라 Thread safety보장할 수도 않을 수도 있고,
    Spring에 의해 구현되는 Singleton은 Thread safety를 자동으로 보장한다.


Singleton :: Stateless

여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 Singleton 객체는 Stateful 하게 설계하면 안되고 Stateless 하게 설계해야 한다.

Stateless (무상태)

  • 특정 클라이언트에 의존적인 필드가 있으면 안된다.
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
  • 가급적 읽기만 가능해야 한다.
  • 필드 대신에 자바에서 공유되지 않는 Local Variable, Parameter, ThreadLocal 등을 사용해야 한다.

public class StatefulService 
{
    private int price; //상태를 유지하는 필드. 있으면 안된다.
    
    public void order(String name, int price) 
    {
        System.out.println("name = " + name + " price = " + price);
        this.price = price; //여기서 문제. price가 변경될 수 있다.
    }
    
    public int getPrice() 
    {
        return price;
    }
}

나는 분명 10000원을 주문했는데 누군가 30000원을 주문했을 때
내 주문금액이 30000원으로 변경될 수 있다.


Singleton :: CGLIB

(교재 p91 참고)
@Configuration 는 바이트코드를 조작한다.
Spring ContainerSingleton Container 이다.
따라서 Spring BeanSingleton이 되도록 보장해야 한다.
그런데 SpringJava code 까지는 못건드리기 때문에 바이트코드를 조작하는 라이브러리를 사용한다(CGLIB).

Config Spring 빈getBean() 메소드를 통해 조회하면 class Config 이 아니라
class Config$$EnhancerBySpringCGLIB$$bcd12d91
이런식으로 뒤에 뭐가 더 붙어 나온다.

SpringCGLIB 라는 바이트코드 조작 라이브러리를 사용해서 Config 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
즉, @Configuration 을 붙이면 바이트코드를 조작하는 CGLIB 기술을 사용해서 Singleton을 보장한다.

@Configuration 없이 @Bean 으로 빈 등록은 가능하지만 바이트조작이 되지 않아 Singleton 보장이 깨진다.

profile
For the 1% inspiration.

0개의 댓글