요즘 자바로는 간단하게 싱글톤으로 구현시켜주는 Enum 클래스가 존재한다.
싱글톤에 대하여 가장 최근에 들어 본 것은 EffectiveJava 3판에 나온 Enum을 활용한 방법이다.
자바에서 기본 적으로 지원해주는 클래스인 Enum클래스의 특성 덕분인데, 해당 클래스의 모든 인스턴스는 Static하게 사용 하고, new는 private으로 함부로 new 를 쓸 수 없으며, 하나의 인스턴스만 공유해서 해주기 때문에 (원래는 Enum을 지원하기 위해 이렇게 우회적으로 만들어졌다.) Enum을 클래스처럼 활용하고 하나의 Enum값(Static한 인스턴스) 를 만들면 싱글턴 처럼 사용 가능해진다.
현업에서는 보통 DI툴을 활용해서 인스턴스 공유 방식으로 싱글톤을 대체하기 때문에 잘 안쓰지만. 토이프로젝트에서 써보고 괜찮으면 적극 활욜 해보려 한다.
원래는 싱글톤 전체 구현에 대한 글을 기록하려 했는데 쓰다보니 Enum 싱글톤에 대한 찬양이 되어버렸네.. 해서 아래부터는 싱글톤들의 발전 과정과 역사다.
1. 기본형
- 초기화 할 때 무조건 객체 할당 하게되고, 이게 싱글턴 클래스끼리 생명주기 에 관해 컨트롤 못해서 순서 없이 동작하도록 짜거나 해야함.
- 누군가 쓰지 않더라도 강제 할당.
public class Singleton{
private Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
...
}
2. lazy 초기화 그리고 쓰레드세이프를 위한 synch
- 성능이 개 후짐.
public class Singleton{ private Singleton instance; private Singleton(){} public static synchronized Singleton getInstance(){ if(instance == null) instance = new Singleton(); return instance; } ... }
3. lazy + 성능 완화를 위한 double 체크 lock
- 생성에 관한 locking만 하기때문에 instantiate과정이 길면 다른 쓰레드가 와서 참조하고 쓸 때 문제가 생길 수 있다. 미완성.
public class Singleton{
private Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
...
}
4. Enum
- 맨처음에 언급해서 엄청 찬양을 하였지만… 결국에 lazy init을 못한다는 얘기는 1번과 같은 문제점이 있을 수 있다는 것.
- 무조건 다른 singleton 클래스와 연관성이 없이 짜야 한다는 것… 또는 초기화 되지 않은 다른 객체를 참조하는 작업 등등이 있을 때 조심해서 써야 한다는것…?
public enum Singleton{
INSTANCE;
...
}
5. layzHolder
- 아직까지는 이게 완전체
- 이 마저도 reflection을 이용한 java class해킹? 방법으로 악의적인 사용자가 singleton을 풀어서 사용 가능
- private inner class는 해당클래스를 감싸고있는 외부 클래스를 참조해서 들어가지 않는 이상 클래스초기화 => static초기화를 하지 않는다.
- 따라서 Singleton.getInstance() 시에 lazyHolder 클래스가 로딩된다.
- lazy하게 singleton 할당 가능하다.
- static이라서 쓰레드에도 안전하다.
public class Singleton{
private Singleton(){}
private static class lazyHolder{
private static final Singleton Instance = new Singleton();
}
public static Singleton getInstance(){
return lazyHolder.Instance;
}
...
}
부록
- 위에 5번 lazyholder 에서 설명 했듯이 reflection과 serialization, clone등을 이용한 공격? 에도 대항하고 막는 등의 치열한 공방의 과정들이 인터넷 여기저기에 널려있다.
- 호불호가 갈리지만 흥미로운사람들도 있을 수 있으니 확인해보자.
-
내가 좋아하는 Enum도 깰 수 있단다 ㅠㅠ 참고 : https://www.javacodegeeks.com/2013/06/singleton-design-pattern-a-lions-eye-view.html
-
결론
- 이러나 저러나 아직 lazyholder만한 놈이 없는 것 같다.
- 쓰고 정리하면서 드는 생각인데 Di로 인스턴스 공유해서 쓰는게 차라리 낫다는 생각이 든다.
- new 는 상속이 안되니깐, 상속해서 뭔가사용 하는거 못한다.
- 테스트 코드 짤 때 저 singleton이 방해가 된다. -> biz로직에 singleton 쓰는 구간 분리해서 쓸 때 상속도 안되니 mock객체같은걸로 갈아끼지도 못한다.
- 진짜 쓸만한 곳만 잘 쓰는게 나을듯….