본문 바로가기
Spring/기본

5. 싱글톤 컨테이너

by wch_t 2023. 8. 4.

1. 싱글톤 패턴

 

1) 정의

 

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

- 따라서, 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.

 

싱글톤 패턴 : 싱글톤 객체를 미리 생성해두는 방법

 

 

2) 문제점

 

- 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.

    → 적용할 모든 클래스를, 위와 같은 형식으로 만들어야 한다.

 

- 의존관계상 클라이언트가 구체 클래스에 의존한다.

    → new() 할 때, 각 구체 클래스로 만듦

     DIP·OCP 위반

 

- 테스트하기 어렵다.

 

- private 생성자로 자식 클래스를 만들기 어렵다.

     상속을 통한 새로운 동작을 추가 및 변경이 어려움

 

- 결론적으로, 유연성이 떨어진다.

     언제나 동일한 객체를 사용해야 하기 때문에 다른 구현을 하는 것이 어려움

 

 

 

 

 

 

 

2. 스프링에서의 해결방법 (Annotation)

 

1) 스프링 컨테이너

 

- 스프링 컨테이너는 위의 싱글톤 패턴의 문제점 없이,

Annotation을 이용해 객체 인스턴스를 스프링 컨테이너에 스프링 빈으로 등록하면서 싱글톤으로 관리한다.

 

*스프링의 기본 빈 등록 방식은 싱글톤이지만, 싱글톤 방식만 지원하는 것은 아니다.

  요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 제공 (이후, Bean Scope 강의에서..)

 

 

 

2) 스프링 싱글톤 설계 주의점

 

- 위의 스프링 컨테이너의 스프링 빈을 통해, 자동으로 싱글톤을 유지해주지만

   설계에 있어 아래의 내용을 매우 유의하여 클래스를 설계해야 한다.

 

- 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하는 것이다.

   따라서, 싱글톤 객체는 항상 무상태(Stateless)로 설계해야 한다!  *매우 중요!!

 

 

무상태(Stateless) 설계

- 특정 클라이언트에 의존적인 필드(코드) x

- 특정 클라이언트가 값을 변경할 수 있는 필드 x

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

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

 

*ThreadLocal

: 스레드 간에 데이터를 공유하지 않고 스레드 내에서 데이터를 저장하고 관리하는 클래스이다.

각 스레드에 독립적인 변수를 만들어서 스레드가 이 변수에 접근할 수 있게 해준다.

이를 통해 멀티스레드 환경에서 각 스레드가 자신만의 데이터를 유지하면서 동시에 안전하게 작업할 수 있도록 도와준다.

 

 

Stateful 필드 수정

 

 

 

3) AppConfig의 구현 DI에서의 싱글톤 유지 

 

Q. Config에서 구현 객체의 DI는 여러 번 일어날 수 있다.

하지만 이 때, 구현 객체를 스프링 빈으로 등록하는 현상이 DI 횟수만큼 일어날까? (싱글톤이 유지될까?)

 

A. 정답을 먼저 얘기하자면, 아니다.

스프링 컨테이너에서 AppConfig 의 복제 빈을 만들어 등록하고, 구현 객체의 중복 Bean 설정을 막는다.

 


구체적인 내부 동작

 

우리는 스프링 컨테이너 생성할 때, Annotation 기반의 자바 설정 클래스 or XML 기반의 설정 정보로

스프링 빈을 등록함을 공부했다. (섹션 4. 스프링 컨테이너와 스프링 빈)

사실, 넘겨주는 AppConfig.class도 @Configuration 로 인해 스프링 빈으로 등록된다.

AppConfg 스프링 빈의 클래스 정보 조회

 

하지만, 스프링은 AppConfig를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한다.

임의의 다른 클래스(AppConfig@CGLIB)가 구현 객체의 싱글톤을 보장하게 해준다.

 

AppConfig@CGLIB 기능

이미 스프링 빈이 스프링 컨테이너에 등록되어 있으면? 기존 스프링 빈 return

없으면? 스프링 컨테이너에 스프링 빈 등록하고 return

 

 

 

따라서, 스프링 설정 정보인 AppConfig.class에 항상 @Configuration을 사용하자.

(AppConfig@CGLIB 를 먼저 등록해 스프링 컨테이너의 싱글톤 유지!!)

 

 

plus.

@Configuration 내부 코드를 살펴보면, proxyBeanMethods() 의 default 값이 true로

위 애노테이션을 붙이면 해당 클래스가 아닌, 프록시 객체(Config@CGLIB)가 스프링 빈으로 등록이 된다.

@Configuration 내부 코드