[Spring] 9-3. 프로토타입 스코프 - 싱글톤 빈과 함께 사용할 때 문제점

송광호·2024년 1월 19일

[Spring]

목록 보기
39/41
post-thumbnail

Spring 시리즈는 혼자 공부하며 기록으로 남기고, 만약 잘못 학습 한 지식이 있다면 공유하며 피드백을 받고자 작성합니다.
스프링에 대해 깊게 공부해보고자 인프런의 김영한 강사님께서 강의를 진행하시는 (스프링 핵심 원리 - 기본편) 강의를 수강하며 정리하는 글입니다.
혹여나 글을 읽으시며 잘못 설명된 부분이 있다면 지적 부탁드리겠습니다.


프로토타입 스코프

  • 앞서 배운 프로토타입 스코프의 빈은 요청하면 항상 새로운 객체 인스턴스를 만들어서 반환해주어야한다.
  • 하지만 싱글톤과 함께 사용할 때 문제점이 있는데 어떤 문제가 있는지 알아보자

프로토타입 빈 스코프 직접 요청

    1. 클라이언트가 스프링 컨테이너에 프로토타입 빈을 요청한다.
    1. 스프링 컨테이너는 프토토타입 빈을 새로 생성해서 반환해준다.
    1. 클라이언트는 반환 받은 빈의 count값을 1 증가시켜준다.

만약 또다른 클라이언트가 요청을 보낸다면?

  • 마찬가지로 @x02 프로토타입 빈의 카운트 또한 1이 될 것이다.
  • 테스트코드로 확인해보자

프로토타입 테스트

public class SingletonWithPrototypeTest1 {

    @Test
    void prototypeFind() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);

        PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
        prototypeBean1.addCount();
        assertThat(prototypeBean1.getCount()).isEqualTo(1);

        PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
        prototypeBean2.addCount();
        assertThat(prototypeBean2.getCount()).isEqualTo(1);
    }

    @Scope("prototype")
    static class PrototypeBean {
        private int count = 0;

        public void addCount() {
            count++;
        }

        public int getCount() {
            return count;
        }

        @PostConstruct
        public void init() {
            System.out.println("PrototypeBean.init " + this);
        }

        @PreDestroy
        public void destroy() {
            System.out.println("PrototypeBean.destroy");
        }
    }
}
  • 복습하는 셈 치고 한번 더 코드로 짜보자 @x01, @x02 둘 다 1값을 가지는걸 확인할 수 있을 것이다.

싱글톤 빈에서 프로토타입 빈을 사용

예시

  • 위의 사진을 보고 다음과 같은 상황을 가정해보자
    1. 클라이언트 빈에서 프로토타입 빈을 요청
    1. 반환 받은 프로토타입 빈은 싱글톤 빈이 관리

    1. 클라이언트가 클라이언트 빈에서 logic() 메서드를 호출
    • logic 메서드는 카운트를 1 증가시키고 카운트 값을 반환받는다.
    1. 클라이언트 빈은 프로토타입 빈의 addCount()메서드를 호출해서 카운트값을 1 증가시킨다.
  • 만약 이때 다른 클라이언트B가 와서 클라이언트 빈의 로직을 호출하면 어떻게 될까?
  • 프로토타입 빈은 싱글톤 빈에서 관리되고 있기 때문에 로직을 실행시키면 카운트 값이 2가 된다.

싱글톤 빈 내부에 프로토타입 빈 사용 테스트 코드

public class SingletonWithPrototypeTest1 {

    @Test
    void singletonClientUsePrototype() {
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);

        ClientBean clientBean1 = ac.getBean(ClientBean.class);
        int count1 = clientBean1.logic();
        assertThat(count1).isEqualTo(1);

        ClientBean clientBean2 = ac.getBean(ClientBean.class);
        int count2 = clientBean2.logic();
        assertThat(count2).isEqualTo(1);
    }

    @Scope("singleton")
    static class ClientBean {
        private final PrototypeBean prototypeBean;

        public ClientBean(PrototypeBean prototypeBean) {
            this.prototypeBean = prototypeBean;
        }

        public int logic() {
            prototypeBean.addCount();
            int count = prototypeBean.getCount();
            return count;
        }
    }

    @Scope("prototype")
    static class PrototypeBean {
        private int count = 0;

        public void addCount() {
            count++;
        }

        public int getCount() {
            return count;
        }

        @PostConstruct
        public void init() {
            System.out.println("PrototypeBean.init " + this);
        }

        @PreDestroy
        public void destroy() {
            System.out.println("PrototypeBean.destroy");
        }
    }
}
  • 한번 테스트 결과를 보자

테스트 결과

expected: 1
 but was: 2
  • 기대했던 값은 1인데 2가 나왔다.
  • 당연하겠지만 이미 싱글톤 빈이 프로토타입 빈을 받아서 계속 가지고있는상태다. 새로 만들어진 빈이 아니라는 소리.

정리

  • 클라이언트 빈을 두개 만들어서 꾸역꾸역 하면 어떻게 될수는 있겠지만 그러면 코드가 너무 지저분해진다.
  • 만약 이런경우 그냥 싱글톤 빈으로 해결을 하지 굳이 프로토타입 빈을 쓰지 않는다.
  • 우리가 원하는 것은 이런것이 아니라 프로토타입 빈을 사용할 때 마다 새로 생성해서 사용하는 것을 원한다.

해결 방법

  • Provider 라는 것은 사용해서 해결하는 방법이 존재한다.

0개의 댓글