[기본기] 9-2. 싱글톤과 함께있는 프로토타입?

khyojun·2022년 10월 11일
0
post-thumbnail

본 게시글은 김영한님의 스프링 핵심 원리 기본편을 정리한 글입니다.


오늘은 프로토타입 빈과 싱글톤 빈을 함께 사용할때 어떻게 되어지는지를 한 번 확인을 해보려고 한다. 근데 이렇게 사용할 경우 예상이 가능하긴 하지만 그렇지만 원하지 않는 결과물을 내는 경우가 있다고 한다. 어떻게 진행이 되길래 이 문제가 일어나는지 한 번 알아보자.

📌 프로토타입 빈을 사용할 경우

우선 프로토타입 빈을 사용할 경우 이제 어떤 값을 count하려고 하였을때 그 값을 출력하는 경우를 한 번 보자.

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

        PrototypeBean bean = ac.getBean(PrototypeBean.class);

        bean.addCount();
        System.out.println("bean.getCount() = " + bean.getCount());

        PrototypeBean bean2 = ac.getBean(PrototypeBean.class);
        bean2.addCount();
        System.out.println("bean2.getCount() = " + bean2.getCount());

        Assertions.assertThat(bean2.getCount()).isEqualTo(1);  // prototype은 새로운 객체를 계속 생성되서 addCount가 1이 되는것이다. prototype이기 때문에
    }
    
    @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");
        }

        @PreDestroy
        public void destory(){
            System.out.println("PrototypeBean.destroy");
        }
    }

위와 같은 경우는 어떤 상황이냐면 이제 Prototype이라는 스코프를 지정하고 이제 bean, bean2라는 각각의 객체를 만드는데 각자 addCount라는 메서드를 통하여서 count를 증가를 시켜서 이제 값이 어떻게 바뀌는지 확인하는 코드이다. 이렇게 되면 어떻게 될까?

bean -> PrototypeBeanx01 을 생성하고 이제 반환을 클라이언트에게 해준다.
bean.addCount = 0 -> 1 로 증가
bean2 -> PrototypeBeanx02
bean2.addCount = 0 -> 1 로 증가


이렇게 진행이 되는데 그도 당연할 것이 애초에 의존관계까지만 주입하고 초기화까지만 담당을 해주고 이제 반환을 해주기 때문에 싱글톤처럼 하나의 객체를 공유하는 것이 아니기에 각자의 Count값은 실제로 별개이다. 그렇기에 서로서로 1을 반환한다는 것을 확인할 수 있었다.

📌 싱글톤과 프로토타입 빈을 같이 사용할 경우

이제 위 처럼 프로토타입 빈을 사용을 하게 되면 빈을 새로 생성하기 때문에 따로 본다고 치면 이제 싱글톤에서의 진행과정은 그렇다면 위와 같은 로직으로 진행하였을때 다음과 같을 거다.

bean -> PrototypeBeanx01 을 생성하고 이제 반환을 클라이언트에게 해준다.
bean.addCount = 0 -> 1 로 증가
bean2 -> PrototypeBeanx01
bean2.addCount = 1 -> 2 로 증가

이렇게 될 것인데 그렇다면 생각해보는것이 위 부제와 마찬가지로 싱글톤과 프로토타입 빈을 같이 사용한다면 어떻게 해야될까? 코드를 통해 확인해보자.

 @Test
    void prototypeInSingleton(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
        ClientBean bean = ac.getBean(ClientBean.class);
        System.out.println("bean.count = " + bean.logic());

        ClientBean bean2 = ac.getBean(ClientBean.class);
        System.out.println("bean2.count =  " + bean2.logic());

        Assertions.assertThat(bean.logic()).isEqualTo(3);

    }

 @Scope("singleton")
    static class ClientBean{

        private PrototypeBean prototypeBean;

        @Autowired
        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");
        }

        @PreDestroy
        public void destory(){
            System.out.println("PrototypeBean.destroy");
        }
    }

그 이럴 경우에는 위에 Test돌려보면 통과가 된다. 그렇다면 어떠한 과정이 있었을까?

이제 0->1로 count가 되는 과정이 있을건데 logic()이라는 메서드를 통하여서 이전 과정과 같이 count를 더하여주는 과정을 진행한다.
1. prototypebean에서 반환 logic() 실행 prototypeBean의 addCount 호출하여 0 -> 1 로 증가
2. prototypebean은 없어졌지만 이제 ClientBean에서 이제 관리를 한다.
3. logic() 실행 prototypeBean의 addCount 호출하여 1->2 로 증가
4. 테스트 확인시 logic()실행 prototypeBean의 addCount 호출하여 2->3 으로 증가

이걸 이렇게 글로만 보면 너무 확인하기 어렵다. 한 번 그림을 확인해보자.

위와 같은 경우에서 그림을 보게되면 이제 clientBean에서 PrototypeBean을 관리하여주면서 count 값을 계속해서 더하는 과정이 진행이 된다. 여기서 우리가 알아야 할 것이 몇 가지 있다.

  • 클라이언트 A와 B가 요청하게 되는 clientBean은 싱글톤이기에 동일한 clientBean을 반환한다.
  • 진짜 진짜 중요한건데 clientBean이 내부에 가지고 있는 프로토타입 빈은 이미 과거에 주입까지 되고 끝난 빈이다. 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성이 되는거지 사용을 할 때마다 계속 생성이 되는 것은 아니다.

이게 약간 느낌상 싱글톤과 함께 프로토타입 빈을 사용을 하게 된다면은 프로토타입은 사라지더라도 싱글톤 빈에서 관리를 해주는 느낌이다. 위 그림처럼 clientBean 안에 PrototypeBean이 포함이되어지는 느낌이다.

그래서 정리해보자면 싱글톤 빈은 생성 시점에 의존관계 주입을 받기 때문에, 프로토타입 빈이 새로 생성이 될 지라도 실제로 싱글톤 빈의 입장에서 본다면 없어진다기보다는 같이 살아가는 느낌이 강해진다. 근데 만약 우리가 프로토타입 빈을 사용하는 입장이라면 생성되고 없어지고 생성되고 없어지고 하는 것을 원하지 계속해서 유지되는 것을 원치는 않았을 것이다. 다음 글에서 이제 이런 문제를 어떻게 해결하는지 알아보자.

오늘의 결론

싱글톤과 프로토타입 빈을 함께 사용을 한다면 프로토타입 빈을 소멸된다고 해도 의존관계 주입이 끝난 상태이기에 싱글톤에 끼여서 프로토타입 빈이 함께 유지가 된다.

프로토타입 빈을 사용하는 목적에 대해서 잘 생각해보자.

출처

  1. 김영한님의 스프링 핵심 원리 기본편(https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8)
profile
코드를 씹고 뜯고 맛보고 즐기는 것을 지향하는 개발자가 되고 싶습니다

0개의 댓글