Skills/Spring

Spring - Singleton(싱글톤)

aoaa 2022. 9. 4. 20:19

1. SIngleton

 Spring은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생했습니다. 대부분 Spring 애플리케이션은 웹 애플리케이션으로 보통의 경우 여러 고객이 동시에 요청을 하게 됩니다.

 이 때, 요청이 여러 개 들어올 때마다 객체를 생성하게 되면, 수천 수만의 요청을 받아내는 서버는 감당하지 못하고 성능이 저하될 것입니다.

이러한 점을 해결하기 위해 Singleton Pattern이 등장합니다.

 싱글톤 패턴은 애플리케이션이 시작 될 때 static을 통해 인스턴스를 메모리에 딱 하나 할당하고, 뒤의 호출 시 마다 해당 인스턴스를 반환해주는 디자인 패턴입니다.. 생성자를 private으로 설정하기 때문에 외부에서 생성자를 통해 인스턴스를 만들 수 없게 됩니다.

 


2. 구현

 

"스프링 없는 순수한  DI 컨테이너 테스트"

 

package hello.core.singleton;

import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class SingleTonTest {

    @Test
    @DisplayName("스프링 없는 순수한 dI 컨테이너")
    void pureContainer() {
        AppConfig appConfig = new AppConfig();

        // 1. 조회 : 호출할 때마다 객체를 생성
        MemberService memberService1 = appConfig.memberService();

        // 2. 조회 : 호출할 때마다 객체를 생성
        MemberService memberService2 = appConfig.memberService();

        // 참조값이 다른 것을 확인

        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        // memberService1 != memberService2
        Assertions.assertThat(memberService1).isNotSameAs(memberService2);
    }
}

 

 

 Test 코드를 실행시키면 위처럼 참조되는 주소값이 다른 것을 볼 수 있습니다.

 

2.1 예시

public class NoteBook {

    private static NoteBook noteBook = new NoteBook();

    public static NoteBook getInstance()
    {
        return noteBook;
    }

    private NoteBook(){};


}

 

 NoteBook이라는 클래스를 간단히 싱글톤 패턴으로 구현한 코드입니다. 보통의 경우 생성자를 public으로 열어 사용자가 직접 인스턴스를 생성해서 사용하도록 하는데, 싱글톤 패턴에서는 프로그램 시작 시점에 인스턴스가 알아서 하나만 만들어집니다.

 

 private NoteBook(){};   // 외부에서 사용 불가 
 private static NoteBook noteBook = new NoteBook();  // 자동으로 인스턴스 생성 

 

static으로 생성된 인스턴스를 가져오기 위해서는 메서드도 static으로 설정해줘야 합니다.

 

public static NoteBook getInstance()
    {
        return noteBook;
    }

 

 

 

NoteBook noteBook1 = NoteBook.getInstance();
NoteBook noteBook2 = NoteBook.getInstance();
System.out.println("noteBook1 is " + noteBook1);
System.out.println("noteBook2 is " + noteBook2);

 

  실제로 인스턴스를 꺼내려면, 위의 코드처럼 NoteBook 클래스에서 직접 메서드를 호출해서 사용하면 됩니다. 싱글톤은 하나의 인스턴스를 공유한다고 하였는데 직접 코드를 실행해서 같은 대상을 가리키는지 확인해보면

 

 

 결과값으로 같은 인스턴스가 출력됐습니다. 싱글톤 패턴에서는 수백 수천개의 인스턴스 요청이 들어와도 결과적으로는 하나의 인스턴스만 반환이 됩니다.

 

 이러한 싱글톤 패턴을 얻을 수 있는 가장 큰 이점은 하나의 인스턴스만 사용함에 따른 메모리 낭비의 방지입니다. 하지만 초당 몇 천, 몇 만건의 요청을 처리하는 대규모 어플리케이션에서는 요청에 따른 인스턴스가 요청 수 이상으로 생성 될 텐데, 비록 자바의 GC 성능이 좋아서 메모리에 부담이 가지않는다고 해도 메모리의 불필요한 낭비를 줄이면 성능이 훨씬 좋아질 것입니다.

 

 


3. 스프링 컨테이너와 싱글톤의 관계

 Spring 컨테이너는 등록된 스프링 빈들을 모두 싱글톤으로 관리합니다. 예를 들면

public class Book {

    private String name;
    private int price;

    public Book(){}
    }

 

@Configuration을 통해 해당 클래스를 설정 정보로 인식하게 만들어주고, @Bean이 붙어있는 메서드의 반환값들을 하나하나 스프링 빈으로 등록합니다.

 

// BookAppConfig.class 
@Configuration
public class BookAppConfig {

    @Bean
    public Book getBook(){
        return new Book();
    }
}

 

 앞에서 스프링 컨테이너는 스프링 빈을 싱글톤으로 관리한다고 하였는데, getBean 메서드를 통해 같은 스프링 빈을 조회했을때 출력값이 같아야 합니다. 

 

// BookAppConfigTest.class 
 ApplicationContext ac = new AnnotationConfigApplicationContext(BookAppConfig.class);
 Book beanA = ac.getBean(Book.class);
 Book beanB = ac.getBean(Book.class);

 // 결과 확인 
 System.out.println("beanA is " + beanA);
 System.out.println("beanB is " + beanB);

 

 

 

 출력값을 보면 beanA와 beanB의 값이 똑같이 나온걸 알 수 있습니다.

 

스프링 컨테이너는 Spring Bean에 등록되는 클래스를 싱글톤으로 만들어줄 필요없이 싱글톤으로 관리해준다는 것을 확인할 수 있었습니다.

 

'Skills > Spring' 카테고리의 다른 글

Spring - Bean 생명주기 콜백  (0) 2022.09.09
Spring - ComponentScan(컴포넌트 스캔)  (0) 2022.09.06
Spring - BeanFactory & BeanDefinition  (0) 2022.09.01
Spring - DI & Spring Container  (0) 2022.08.31
Spring - 테스트 코드(TDD) 작성  (0) 2022.08.02