본문 바로가기
디자인패턴

생성 패턴

by 이상한나라의개발자 2023. 12. 12.

싱글톤 패턴

 

싱글톤 패턴은 특정 클래스의 인스턴스가 하나만 생성 되도록 보장하고, 그 인스턴스에 쉽게 접근할 수 있는 글로벌 포인트를 제공하는 패턴입니다. 싱글톤 패턴 설정은 로거, 캐시, 스레드 풀, 데이터베이스 연결 등과 같이 한번만 생성되어 여러 곳에서 공유되어야 하는 객체에서 주로 사용됩니다.

 

1. 즉시 초기화

 

public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

 

 

2. Lazy Initialization with Double Checked Locking

 

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

 

 

3. Static Inner Class (정적 내부 클래스)

 

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

 

 

스프링에서의 싱글톤

 

스프링에서의 싱글톤은 자바에서의 전통적인 싱글톤 디자인 패턴과는 조금 다르게 동작합니다. 스프링에서 싱글톤이라는 말은 스프링의 빈 생명 주기와 관련이 있습니다.

 

 

1. 스프링 컨테이너 범위에서의 싱글톤 

 

스프링에서 싱글톤이라는 것은 전체 JVM 범위 내에서의 유일한 인스턴스를 말하는 것은 아니며, 특정 스프링 컨테이너 ( ApplicationContext or BeanFactory) 범위 내에서 유일한 인스턴스를 보장한다는 의미 입니다. 따라서 스프링 컨테이너를 가진 어플리케이션에서는 동일한 빈 이름으로 여러 싱글톤 인스턴스가 존재할 수 있습니다.

 

 

package com.example;

import org.springframework.stereotype.Component;

@Component
public class SingletonBean {
    public String getMessage() {
        return "Instance: " + this;
    }
}


-----

package com.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context1 = new AnnotationConfigApplicationContext();
        context1.scan("com.example");
        context1.refresh();

        AnnotationConfigApplicationContext context2 = new AnnotationConfigApplicationContext();
        context2.scan("com.example");
        context2.refresh();

        SingletonBean beanFromContext1 = context1.getBean(SingletonBean.class);
        SingletonBean beanFromContext2 = context2.getBean(SingletonBean.class);

        System.out.println(beanFromContext1.getMessage());
        System.out.println(beanFromContext2.getMessage());

        context1.close();
        context2.close();
    }
}

 

위 코드에서 beanFromContext1, beanFromContext2는 각각 다른 ApplicationContext에서 가져오므로 SingletonBean의 인스턴스 또한 다르게 생성됩니다. ( 참조 주소 다름 )

 

 

2. 빈의 생명주기 

 

스프링에서 빈의 기본 스코프는 싱글톤 입니다. 즉, @Component, @Service, @Controller, @Repository 등의 어노테이션을 사용하여 빈으로 동록되는 클래스를 기본적으로 싱글톤 스코프를 가지게 됩니다. 스프링 컨테이너는 이러한 싱글톤 빈의 인스턴스를 처음 요청시 생성하고 그 이후의 요청에 대해서는 동일한 인스턴스를 반환합니다.

 

package com.example;

import org.springframework.stereotype.Service;

@Service
public class SingletonService {
    public void printMessage() {
        System.out.println("This is a singleton bean in Spring: " + this);
    }
}


package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class BeanUser {
    @Autowired
    private SingletonService singletonService;
    
    public void useService() {
        singletonService.printMessage();
    }
}


package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SingletonExampleApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SingletonExampleApplication.class, args);
        
        BeanUser beanUser1 = context.getBean(BeanUser.class);
        beanUser1.useService();
        
        BeanUser beanUser2 = context.getBean(BeanUser.class);
        beanUser2.useService();
    }
}

 

 

이 코드를 실행하면 SingletonService의 printMessage() 메서드가 두번 호출 되지만, 출력되는 참조주소는 동일하게 나타나게 됩니다. SingletonService 빈이 싱글톤 스코프를 가지며, 동일한 인스턴스가 재사용되고 있습니다.