스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 생성한다.

 

 

 

싱글톤 패턴

서블릿 클래스당 하나의 오브젝트만 만들어두고,

 

사용자 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 공유해 동시에 사용한다.

 

이렇게 애플리케이션 안에 제한된 수의 오브젝트만 만들어서 공유해서 사용하는 것이 싱글톤 패턴의 원리이다.

 

 

 

자바의 싱글톤 패턴을 구현하는 방법

1. 클래스 밖에서 오브젝트를 생성하지 못하게 private로 생성자를 만든다.

2. 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 static 필드를 정의한다.

3. 스태틱 팩토리 메소드인 getInstance() 를 만들고, 이 메소드가 최초로 호출되는 시점에서 한번만 오브젝트가 만들어지게 한다.

-> 생성된 오브젝트는 static 필드에 저장된다.

4. 싱글톤 오브젝트가 만들어지고 난 후에는 getInstance 메소드로 이미 만들어진 static 필드에 저장해둔 오브젝트를 넘김

 

public class Dao {
	private static Dao INSTANCE;
    
    private String name;
    
    private Dao(String name){
    	this.name = name;
    }
    
    public static synchronized Dao getInstance(String name){
    	if(INSTANCE == null) INSTANCE = new Dao(name);
        return INSTANCE;
    }
}

 

 

 

자바의 싱글톤 패턴의 단점

1) private 생성자를 갖고있어 상속할 수 없다.

2) 만들어지는 방식이 제한적이라 테스트하기 어렵다.

3) 서버환경에서는 여러 JVM에 분산돼서 설치되는 경우에도 각각의 독립된 오브젝트가 생성되 싱글톤 가치가 떨어진다.

4) 전역 상태로 사용되기 쉬워 객체지향에서 권장하지 않는 프로그래밍 방법이다.

 

 

 

싱글톤 레지스트리

하지만 스프링에서는 서버환경에서 싱글톤 방식을 적극 지지한다.

그리고 이를 스프링에서는 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다.

 

스프링 컨테이너는 싱글톤을 생성,관리,공급하는 싱글톤 관리 컨테이너의 역할도 한다.

이 스프링 컨테이너는 위의 static 메소드와 private 생성자 없이도 클래스를 싱글톤으로 활용할 수 있게 해준다.

그래서 관계설정, 컨테이너를 사용한 생성 등에 대한 제어권을 컨테이너에게 넘기는 이유중에 하나도 이 점 때문이다.

 

 

예제)

public class UserDao {

    private String test;

    public UserDao(String test) {
        this.test = test;
    }

}
@Component
public class DaoFactory {

    @Bean
    public UserDao userDao() {
        return new UserDao("test");
    }

}
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);

        UserDao userDao1 = context.getBean("userDao", UserDao.class);
        UserDao userDao2 = context.getBean("userDao", UserDao.class);

        System.out.println("userDao1: " + userDao1);
        System.out.println("userDao2: " + userDao2);

        DaoFactory daoFactory = new DaoFactory();

        UserDao userDao3 = daoFactory.userDao();
        UserDao userDao4 = daoFactory.userDao();

        System.out.println("userDao3: " + userDao3);
        System.out.println("userDao4: " + userDao4);
    }

 

위 결과 값은 아래와 같다.

userDao1: com.example.demo.dto.UserDao@32502377
userDao2: com.example.demo.dto.UserDao@32502377
userDao3: com.example.demo.dto.UserDao@2c1b194a
userDao4: com.example.demo.dto.UserDao@4dbb42b7

 

스프링이 생성하는 빈 오브젝트는 모두 같은 객체임을 알 수 있고,

팩토리를 통해 빈 오브젝트를 생성하면 서로 다른 객체임을 알 수 있다.

 

 

싱글톤의 오브젝트 상태 관리

멀티스레드 환경에서 싱글톤 오브젝트는 여러 스레드가 동시에 접근해서 사용할 수 있다.

따라서 상태 관리에 주의를 요구한다.

 

if. 멀테스레드 환경에서 서비스 형태의 오브젝트를 사용하는 경우

then. 상태 정보를 내부에 갖고 있지 않은 stateless 방식으로 만들어져야한다.

 

그 이유는 다중 사용자 요청을 여러 스레드가 동시에 싱글톤 오브젝트 인스턴스 변수를 수정하는것은 위험하기 때문이다.

저장할 공간이 하난데 서로 값을 덮어쓰고 저장하지 않은 값이 읽어 올 수 있기 때문이다.

 

단, 읽기전용의 정보는 인스턴스 변수로 정의해서 사용하는 것에 문제가 없다.

 

* stateless: 인스턴스 필드의 값을 변경하거나 상태 유지를 하지 않는 방식

 

 

 

 

 

+ Recent posts