[스프링 이해와 원리] #3. 싱글톤 레지스트리와 오브젝트 스코프 순서3 :: 잡다한 프로그래밍
반응형

1. 직접 생성한 Factory와 스프링 Factory의 차이점

직접 생성하게된 Factory는 호출할때 마다 오브젝트가 달라지고 스프링 Factory는 호출해도 같은 오브젝트를 리턴한다. 이는 스프링 Factory는 싱글톤 패턴으로 구성되어 있기 때문이다.

 

2. 싱글톤 레지스트리로서의 애플리케이션 컨텍스트

스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 싱글톤 오브젝트로 생성한다. 애플리케이션 컨텍스트는 이러한 싱글톤 객체를 저장하고 관리하는 레지스트리 역할을 한다. 이때 스프링에서의 싱글톤은 JAVA 싱글톤 디자인패턴과 비슷한개념이지만 구현방법은 다르다.

 

싱글톤을 사용하는 이유는 스프링이 처음 설계 되었을때 서버에서 수백 수천번씩 요청을 받아 처리 할 환경이 필요했는데 이를 요청이 올때마다 오브젝트를 생성한다고 가정하면 오브젝트 생성이 너무 많아지고, 가비지 컬렉션의 성능이 좋아짐에도 서버에 부하가 온다 따라서 싱글톤 패턴을 선택하였다.

 

기존 JAVA 싱글톤 패턴

  1. 기본방법

     사용자가 getInstance를 통해 Single 객체를 사용할 때 객체가 null이면 생성하고 null이 아니면 기존의 것을 가져다 사용하는 방식. 하지만 멀티 스레드 환경에서 두개의 스레드가 null일때 접근하면 하나만 생성되지 않을 수 있음.

public class Single {
	private static Single single;
	
	private Single() {};
	
	public static Single getInstance() {
		if(single == null) {
			single = new Single();
		}
		return single;
	}
}

 

  2. synchronized를 이용한 방법

     이 방법을 이용하면 멀티스레드 환경에 적절하게 대응 할 수 있으나 속도가 너무 느리다는 단점이 있다.

public class Single {
	private static Single single;
	
	private Single() {};
	
	public static synchronized Single getInstance() {
		if(single == null) {
			single = new Single();
		}
		return single;
	}
}

 

   3. volatile 이용하기

다음과 같은 volatile을 이용해 미리 객체를 선언하고 이후에 getInstance를 이용한 방식이다. 만약 Single을 사용하지 않을 경우에도 객체가 생성되어 있다는 단점이 있다.

public class Single {
	private static volatile Single single =  new Single();
	
	private Single() {};
	
	public static synchronized Single getInstance() {
		return single;
	}
}

 

  4. holder 이용하기

다음과 같은 내부 클래스를 이용한 방법으로 싱글톤을 구현할 수 있다. Single이 로드되지 않으면 먼저 생성되어있는 객체도없을 뿐더러, getInstance가 불릴때 Holder클래스가 로드되면서 single객체를 생성하므로 스레드에도 안전하다.

public class Single {

	private Single() {};
	
	private static class Holder {
		private static final Single single = new Single();
	}
	
	public static Single getInstance() {
		return Holder.single;
	}
}

 

기존 JAVA 싱글톤 패턴의 단점

  1. private 생성자를 가지고 있어 상속할 수 없다
  2. 테스트하기에 어렵다 (단위 테스트에 안좋음)
  3. 서버환경에서 싱글톤이 하나만 만들어지는것을 보장하지 못한다(여러 JVM에 분산설치될 경우)
  4. 싱글톤의 사용은 전역 상태를 만들 수 있어 바람직하지 못하다. (public static이므로)

이러한 단점으로 인해 스프링에서는 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데 이를 싱글톤 레지스트리 라고 한다.

싱글톤 레지스트리는 JAVA 싱글톤과 달리 ApplicationContext가 로드될때 IOC/DI를 통해 빈을 생성하고 빈목록에 이를 저장해 놓고 이후에 ApplicationContext를 이용해 빈을 가져다가 사용하는 방식으로 되어있다. 즉 ApplicationContext가 싱글톤 레지스트리로서의 역할을 한다.

 

싱글톤 레지스트리의 장점

private static 이 아닌 평범한 자바 클래스를 싱글톤처럼 활용할 수 있게 한다. 이는 public 생성자를 가질 수 있고 앞서나온 단점이 해결된다.

 

3. 싱글톤과 오브젝트의 상태

이러한 싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근할 수 있는 문제가 생기므로 상태정보를 가지고 있지 않은 stateless방식으로 만들어져야한다.

만약 다수의 스레드가 싱글톤 오브젝트의 변수를 수정하게 되면 서로 값을 덮어쓰고 자신이 저장한 값이 아닌 값을 읽어올 수 있기 때문에 기본적으로 인스턴스 필드의 값을 변경하고 유지하는 상태유지 방식으로 만들지 않는다. 

 

따라서 DB나 서버의 리소스로부터 생성한 정보는 파라미터, 로컬 변수, 리턴 등으로 해결하면 된다.

 

다음과 같은 문제가 생기는 Dao가 있다 싱글톤으로 빈이 만들어지는데 Connection 변수와, User를 로컬변수가 아닌 private 변수로 선언했다.

이렇게 되면 User와 Connection는 매번 새로운 값으로 값이 바뀌는 상태정보를 가지고있으므로 문제가 생길 수 있으므로 잘못된 방법이다. 하지만 connectionMaker는 처음에만 값이 정해지고 이후는 읽어서 사용하는 읽기정보이므로 사용해도 무방하다.

 

싱글톤에서 문제가 생기는 UserDao

public class UserDao {
    private ConnectionMaker connectionMaker;
    private Connection c;// 문제가 되는 부분
    private User user; //문제가 되는부분
    
    public User get(String id) throws ClassNotFoundException, SQLException {
    	this.c = connectionMaker.makeConnection();
    	...
    }
}

 

반응형

+ Recent posts