싱글톤 컨테이너

udonehn·2023년 10월 28일
0

웹 애플리케이션과 싱글톤

  • 웹 애플리케이션은 클라이언트의 요청이 들어올 때마다 객체를 생성하기 때문에 비효율적이다.
    • 해당 객체를 1개만 생성하고, 공유하도록 설계하면 효율적이다. -> 싱글톤 패턴

SingletonTest

Test의 core 폴더 아래 Singleton 패키지를 생성하고, SingletonTest 클래스를 생성한다.

<코드>

  • memberService1memberService2은 다른 객체임을 알 수 있다.
    • 매번 다른 객체가 생성된다.

싱글톤 패턴

싱글톤 패턴이란 클래스의 인스턴스가 딱 1개만 생성되는 디자인 패턴을 의미한다.

SingletonService

테스트 폴더의 Singleton 패키지 아래 SingletonService 클래스를 생성한다.

<코드>

public class SingletonService {
    private static final SingletonService instance = new SingletonService();
    public static SingletonService getInstance(){
        return instance;
    }
...
  • private static final SingletonService instance = new SingletonService(); : 자바가 실행될 때 자동으로 실행되어 자기 자신의 객체를 생성하여 instance에 참조를 넣어 둔다.
    • private을 사용하였기 때문에 다른 곳에서는 new 키워드를 통해 생성할 수 없다.
      • 좋은 코드는 컴파일 오류만으로 해결할 수 있는 코드이다.
  • 조회할 때에는 오직 getInstance()를 통해서만 조회할 수 있다.

singletonTest

singletonTest 클래스에 다음을 추가한다.

    @Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    void singletonServiceTest(){
        SingletonService singletonService1 = SingletonService.getInstance();
        SingletonService singletonService2 = SingletonService.getInstance();

        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);
    }
  • 실행 결과 같은 객체 인스턴스가 반환되는 것을 알 수 있다.
    • assertThat(singletonService1).isSameAs(singletonService2);를 통해 확인할 수도 있다.

하지만 싱글톤 패턴은 코드가 복잡해지고 유연성이 떨어지는 등 여러 문제점이 있기 때문에 스프링 컨테이너가 필요하다.

싱글톤 컨테이너

  • 스프링 컨테이너는 객체 인스턴스를 기본적으로 싱글톤으로 관리한다.
    • 스프링을 이용하면 코드가 간단해진다. (싱글톤 패턴의 문제점 또한 해결할 수 있다.)

SingletonTest 추가

    @Test
    @DisplayName("싱글톤 컨테이너와 싱글톤")
    void springContainer(){
        //AppConfig appConfig = new AppConfig();
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        MemberService memberService1 = ac.getBean("memberService", MemberService.class);
        MemberService memberService2 = ac.getBean("memberService", MemberService.class);

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

        assertThat(memberService1).isSameAs(memberService2);
    }

실행 결과 같은 객체를 사용함을 알 수 있다.
고객의 요청이 올 때 마다 새로운 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유하여 효율적으로 재사용한다.

싱글톤 방식의 주의점

싱글톤 패턴으로 설계를 할 때에는 무상태(stateless)로 설계해야 한다.

  1. 특정 클라이언트에 의존적인 필드가 있으면 안 된다.
  2. 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안 된다.
  3. 가급적 읽기만 가능해야 한다.
  4. 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용하여야 한다.

StatefulService

test 폴더의 Singleton 패키지 아래 StatefulService 클래스를 생성한다.

<코드>

StatefulServiceTest

test 폴더의 Singleton 패키지 아래 StatefulServiceTest 클래스를 생성한다.

<코드>

실행 결과 사용자A의 주문 금액이 20000원이라는 결과가 나왔다. StatefulServiceprice필드가 공유되는데, 클라이언트가 값을 변경하기 때문이다.
공유필드는 조심하여야 한다.

@Configuration과 싱글톤

의문
1. AppConfig에서 memberService()memberRepository()를 통해 MemoryMemberRepository()를 호출한다.
2. 그리고 orderService() 또한 memberRepository()를 통해 MemoryMemberRepository()를 호출한다.
3. 다른 2개의 MemoryMemberRepository()가 생성되는 듯 하는데, 싱글톤이 깨지지 않을까?

configurationTest

test폴더의 Singleton 패키지 아래 클래스를 생성한다.

<코드>

실행 결과 같은 인스턴트가 공유되어 사용되는 것을 알 수 있다.

AppConfig

println을 통해 Bean 함수가 1번씩만 호출되었다.

<코드>

  • 실행 결과 1번만 실행된다는 것을 알 수 있다.
    • 스프링은 싱글톤을 보장한다.

@Configuration과 바이트코드 조작의 마법

  • Test 폴더의 Singleton 패키지 아래 configurationDeep 클래스를 추가한다.
    • configurationTest.java 파일 아래 추가한다.
    @Test
    void configurationDeep(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        AppConfig bean = ac.getBean(AppConfig.class);

        System.out.println("bean = " + bean.getClass());
    }
  • 실행 결과: bean = class hello.core.AppConfig$$SpringCGLIB$$0
    • 일반적인 클래스와는 다른 것을 알 수 있다.

정리

  • 스프링은 CGLIB라는 바이트코드 조작 라이브러리를 통해 @Configuration이 사용된 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 이를 스프링 빈으로 등록한다.
  • @Bean이 붙은 메소드가 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성하여 등록하여 반환하는 코드가 동적으로 만들어진다.
  • @Configuration을 사용하지 않아도 동작하지만, 스프링 컨테이너가 관리하지 않아 싱글톤을 보장하지 않기 때문에 바람직하지 않다.

본 포스팅은 김영한 강사의 스프링 핵심 원리 강의를 수강하고 요약한 것으로, 해당 강의의 영상 및 강의자료를 참고하였습니다.

profile
안녕하세요. 만나서 반갑습니다.

0개의 댓글

관련 채용 정보