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

박찬우·2023년 12월 20일
0

스프링

목록 보기
24/88
post-thumbnail

싱글톤 빈인 ClientBean이 PrototypeBean을 사용(문제)

  • 예시
  1. 스프링 컨테이너 생성 시점에 ClientBean 빈 생성 후 clientBean에 의존 관계 주입
  2. PrototypeBean 빈 생성 후 prototypeBean을 clientBean에 반환(prototypeBean의 count = 0)
  3. clientBean의 logic() 호출
  4. prototypeBean의 addCount() 호출
  5. count가 1 늘어남(prototypeBean의 count = 1)
  • 예시를 클라이언트 2명이 하게된다면?
  1. ClientBean 빈 생성 시점에 이미 PrototypeBean이 지정 되기 때문에 클라이언트가 2명이여도 결국은 같은 PrototypeBean을 가지게 된다
  2. 따라서 의존성 주입을 따로하고나서 logic()을 호출하여도 같은 빈을 반환받기 때문에 값은 2가 된다
    (prototypeBean의 count = 2)
  • 예시
package hello.core.scope;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

public class SingletonWithPrototypeTest1 {

    @Test
    void singletonClientUsePrototype() {
        // ClientBean 빈 생성
        // 생성 시점에 PrototypeBean 생성 count = 0
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);

        // ClientBean 빈 주입 후 login() count = 1
        ClientBean clientBean1 = ac.getBean(ClientBean.class);
        int count1 = clientBean1.logic();
        Assertions.assertThat(count1).isEqualTo(1);

        // ClientBean 빈 주입 후 login() count = 2
        ClientBean clientBean2 = ac.getBean(ClientBean.class);
        int count2 = clientBean2.logic();
        Assertions.assertThat(count2).isEqualTo(2);
    }

    @Scope("singleton")
    static class ClientBean {
        // 생성 시점에 주입
        private final PrototypeBean prototypeBean;

        @Autowired
        public ClientBean(PrototypeBean prototypeBean) {
            this.prototypeBean = prototypeBean;
        }
        public int logic() {
            prototypeBean.addCount();
            int count = prototypeBean.getCount();
            return count;
        }
    }

    @Configuration
    @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("init");
        }

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

싱글톤 빈인 ClientBean이 PrototypeBean을 사용(해결) - DL

  • 새로운 PrototypeBean을 받고 싶은 경우 의존관계를 외부에서 주입(DI) 받는게 아니라 이렇게 직접 필요한 의존관계를 찾으면 되는데 이것을 Dependency Lookup (DL) 의존관계 조회(탐색) 이라한다
  • 의존관계주입은 스프링컨테이너 생성 시점에 주입을 하고 의존관계조회는 원하는 시점에 주입을 하는 것
  • 예시
@Autowired 
private ApplicationContext ac;

public int logic() {    
  PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
  prototypeBean.addCount();
  int count = prototypeBean.getCount();
  return count; 
}
  • 그러나 위 방법은 스프링 컨테이너에 종속적인 코드가 되고, 단위 테스트도 어려워진다.

싱글톤 빈인 ClientBean이 PrototypeBean을 사용(해결) - ObjectProvider

  • 지정한 빈을 컨테이너에서 대신 찾아주는 DL 서비스를 제공하는 것
  • 예시
@Autowired 
private ObjectProvider<PrototypeBean> prototypeBeanProvider;

public int logic() {    
  PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
  prototypeBean.addCount();
  int count = prototypeBean.getCount();
  return count;
}
  • 스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워진다.
  • 그러나 여전히 스프링에 의존적임

싱글톤 빈인 ClientBean이 PrototypeBean을 사용(해결) - Provider

  • 똑같은 DL 서비를 제공하는 JSR-330 자바 표준을 사용하는 방법이다
  • 보통 이 방법을 사용함(떄에 따라 다름)
  • 스프링부트 3.0 미만
    javax.inject:javax.inject:1 라이브러리를 gradle에 추가해야 한다.
  • 스프링부트 3.0 이상 jakarta.inject:jakarta.inject-api:2.0.1 라이브러리를 gradle에 추가해야 한다.
  • 예시
@Autowired 
private Provider<PrototypeBean> provider;

public int logic() {    
  PrototypeBean prototypeBean = provider.get(); 
  prototypeBean.addCount();
  int count = prototypeBean.getCount();
  return count; 
}
  • 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용할 수 있고, 기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워진다.
profile
진짜 개발자가 되어보자

0개의 댓글