싱글턴 패턴(Singleton Pattern) ; 유일무이 인스턴스

싱글턴 패턴은 해당 클래스의 인스턴스가 단 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴이다.

싱글턴 패턴의 장점

싱글턴 패턴은 사실 그렇게 특이한 패턴은 아니다. 하지만, 시스템에서 단 한개만 있으면 유리한 객체들이 있다.

예를 들어, DB 커넥션 풀, 스레드 풀, 캐시, 사용자 설정 같은 것들은 여러 개의 인스턴스가 생길 경우
시스템 오류를 야기시킬 수 있고, 인스턴스를 생성 / 파괴 하는데 많은 리소스를 잡아먹게된다.

따라서, 이런 종류의 객체들은 싱글턴 패턴을 적용하여, 시스템에 단 한개의 인스턴스만 존재하도록 강제할 수 있다.

전역변수 vs 싱글턴 패턴

  • 전역변수는 Lazy 하게 인스턴스를 생성할 수 없음
  • 싱글턴은 인스턴스가 유일하도록 강제한다는 것에 의의가 있음

싱글턴 패턴 구현하기

getInstance() with 동기화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private static Singleton uniqueInstance;

private Singleton() {

}

public static synchronized Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
  • 최초 생성 시에만 동기화가 유효 하다는 단점
  • 성능 저하

Singleton 인스턴스를 최초 실행 시 생성

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton uniqueInstance = new Singleton();

private Singleton() {

}

public static Singleton getInstance() {
return uniqueInstance;
}
}
  • 클래스가 로딩될 때 JVM에서 Singleton의 유일한 인스턴스를 생성해 준다.
  • 멀티스레딩 환경에서도 단 하나의 인스턴스만 유지할 수 있다.
  • Lazy 인스턴스 생성은 불가능하다.

DCL(Double-Checking Locking) 을 써서 동기화되는 부분을 줄인다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {
private volatile static Singleton uniqueInstance;

private Singleton() {

}

public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
  • synchronized를 메소드에 적용한 것 대비 속도 측면에서 오버헤드를 극적으로 줄일 수 있다

Spring Boot 에서 찾은 Singleton

  • Spring 에서 모든 bean 은 기본적으로 Singleton 으로 생성되는 것을 보장한다.

Spring Configuration annotation docs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Specify whether {@code @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even
* in case of direct {@code @Bean} method calls in user code. This feature
* requires method interception, implemented through a runtime-generated CGLIB
* subclass which comes with limitations such as the configuration class and
* its methods not being allowed to declare {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/

정리해보면 CGLIB 라는 프록시 라이브러리를 사용하는데, Spring 에서 프록시 대상이 되려면
@Bean 같은 어노테이션 적용이 필요하다.

생각해보자

Spring Bean 을 왜 싱글톤으로 유지하는 지 궁금해져서 조금 찾아봤다.

Spring의 기본 철학은 데이터 객체가 아닌 서비스를 작성하는 데 도움이 되도록 설계되었다는 것이다.
따라서, 개발자는 관계형 데이터 객체 또는 도메인 객체같은 POJO 타입의 객체를 Spring 이 관리하는 service, repository, controller
에 전달하여, 효율적으로 관리해주는 것이 그 목적이다.

service, repository, controller 같은 객체들은 상태를 저장할 필요가 없으며, 스레드 세이프하게 동작하기 위해
싱글톤으로 인스턴스가 생성되도록 설계되었다.

Action Items

  • CGLIB
  • 프록시 객체

참고자료