본문 바로가기

Programmer/JAVA

자바 싱글톤 vs 스프링 싱글톤의 차이점

이 주제는 스프링 개발자들이 싱글톤 개념에 대한 이해를 확인하기 위해 인기 있는 인터뷰 질문 중 하나이다. 

싱글톤 패턴이란

상황에 따라서 Application에서 특정 클래스에 대해 하나의 객체만 존재해야 하는 경우가 있습니다. 예를 들어 특정 세션에 대해 하나의 데이터베이스 연결만 존재해야 하고, 여러 모듈에서 공유되고 있는 글로벌 속성 집합을 참조하는 개체의 경우가 그 대표적인 케이스입니다. 

이러한 클래스의 유일한 목적은 클래스의 객체가 전체 응용프로그램에서 하나만 생성되고, 여러 클라이언트에서 공유되어야 합니다. 

# Code 1

package tips.pattern.singleton;

public class MyConnection {
  private static MyConnection connection = new MyConnection();
  
  private MyConnection() {
  }
  
  public static MyConnection getConnection() {
    return connection;
  }
}

생성자를 private 선언하여 외부에서 이 클래스를 직접 인스턴스화할 수 없도록 했습니다. 

유일하게 호출할 수 있는 방법은 static method인 MyConnection.getConnection() 을 통한 방법입니다. 

# Code 2

package tips.pattern.singleton;

public class MyConnectionConsumer {
  public static void main(String[] args) {
    MyConnection connection1 = MyConnection.getConnection();
    MyConnection connection2 = MyConnection.getConnection();
    if (connection1 == connection2) {
      System.out.println("Both the references are pointing to the same object");
    }
    else {
      System.out.println("Objects are not equals");
    }
  }
}

위의 코드로 증명할 수 있습니다. 

Code 1 에는 한가지 문제점이 있습니다. Client가 MyConnection을 사용하지 않는다면? connection 객체가 static 개체로 관리되고 있기 때문에 MyConnection class가 로딩되는 시점에 객체가 로딩될 것입니다. 

# Code 3 - 필요할때 connection 이 생성되도록 변경

package tips.pattern.singleton;

public class MyConnection {
  private static MyConnection connection = null;
  
  private MyConnection() {
  }
  
  public static MyConnection getConnection() {
    initObject();
    return connection;
  }
  
  private static void initObject() {
    if (connection == null) {
      connection = new MyConnection();
    }
  }
}

Code 3는 멀티스레드 환경에서 잘 동작할까?라고 생각했을 때 잘 동작하지 않을 것입니다. 

동시에 MyConnection.getConnection()을 호출한다면? 문제가 될 것이라 이걸 수정해보면 다음과 같이 될 것입니다. 

# Code 4

private static void initObject() {
  synchronized (MyConnection.class) {
    if (connection == null) {
      connection = new MyConnection();
    }
  }
}

Thread Safe에 대한 Bill Pugh 싱글톤 패턴도 있습니다. 

# Code 5

public class MyConnection {
  private MyConnection() {
  }
  
  private static class MyConnectionHolder {
    static final MyConnection CONNECTION = new MyConnection();
  }
  
  public static MyConnection getConnection() {
    return MyConnectionHolder.CONNECTION;
  }
}

가장 보편적으로 사용되는 싱글톤 구현법이며, static 키워드로 선언된 객체는 메모리 할당이 단 한 번만 이루어지고, final 키워드로 선언되면 그 값을 덮어쓸 수 없음을 이용한 방법입니다. 


Java Singleton 클래스는 classloader 단위이며, Spring Singleton 은 Application context 단위입니다. 

Java 싱글톤

싱클턴 클래스를 구현하기 위해 사전 정의된 규칙 같은건 없지만 클래스는 클래스 로더마다 한 번만 인스턴스화 해야 합니다. 

  • 싱글턴 클래스라고 하면 해당 클래스의 인스턴스를 하나만 만들수 있습니다. 
  • 외부 클래스로부터 인스턴스화 되는 것을 막기 위해 생성자는 private으로 선언되어야 합니다. 
  • 인스턴스화 된 클래스를 static 변수로 선언합니다. 
  • 인스턴스화 된 클래스를 리턴하는 함수를 선언합니다. 

Spring 싱글톤

Spring에서는 컨테이너 내에서 특정 클래스에 대해서 @Bean이 정의되면 스프링 컨테이너는 그 클래스에 대해서 딱 한 개의 인스턴스를 만듭니다. 이 공유 인스턴스는 설정 정보에 의해서 관리되고, Bean이 호출될 때마다 스프링은 생성된 공유 인스턴스를 리턴 시킵니다. 

Bean의 관리주체인 스프링 컨테이너는 항상 단일 공유 인스턴스를 리턴시키므로 Thread Safety 도 자동으로 보장됩니다. 

요약하면, 

  • Java 의 싱글톤은 클래스 로더에 의해 구현되고, 스프링 싱글톤은 스프링 컨테이너에 의해서 구현된다. 
  • Java 싱글톤의 scope는 코드 전체이고, 스프링 싱글톤의 scope는 해당 컨테이너 내부입니다. 
  • 스프링에 의해 구현된 싱글톤패턴은 Thread Safety를 자동으로 보장합니다. 하지만 Java 싱글톤의 경우 구현하는 개발자의 로직에 따라 보장할 수도 않을 수도 있습니다. 

 

 

'Programmer > JAVA' 카테고리의 다른 글

자바 싱글톤 vs 스프링 싱글톤의 차이점  (0) 2020.07.26