빈 스코프(Bean Scope)와 프로토타입 스코프(Prototype Scope), 싱글톤에서의 프로토타입 스코프의 처리

2022. 6. 3. 20:37·Back-End/Spring

1. 빈 스코프(Scope)란?

→ 빈이 존재할 수 있는 범위

스프링이 지원하는 스코프

  • 싱글톤(Singleton) : 기본 스코프, 스프링 컨테이너 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  • 프로토타입(Prototype) : 스프링 컨테이너가 빈의 생성과 의존관계 주입 까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
  • 웹 관련 스코프 : request, session, application

2. 프로토 타입 스코프

  • 항상 새로운 인스턴스를 생성해서 반환
  • 빈을 생성, 의존관계 주입, 초기화 까지만 처리
    → @PreDestory 같은 종료 메서드가 호출되지 않는다.
public class PrototypeTest {

    @Test
    void prototypeBeanFind() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
        System.out.println("find prototypeBean1");
        PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
        System.out.println("find prototypeBean2");
        PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
        System.out.println("prototypeBean1 = " + prototypeBean1);
        System.out.println("prototypeBean2 = " + prototypeBean2);
        assertThat(prototypeBean1).isNotSameAs(prototypeBean2);

        ac.close();  /* 스프링 컨테이너가 종료되지만, 각 빈의 인스턴스는 사라지지 않기때문에
                        수동 destroy() 메서드를 호출해야한다. */
        //소멸자 호출 코드
        //prototypeBean1.destroy();
        //prototypeBean2.destroy();
    }

    @Scope("prototype")
    static class PrototypeBean {

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

        @PreDestroy
        public void destroy() {
            System.out.println("PrototypeBean.destroy");
        }
    }
}
  • @Scope(”prototype”) 으로 프로토타입 스코프 지정
  • 소멸자 수행을 워하면 직접 호출 해야한다.
    → prototypeBean1.destroy(); 코드 작성

스프링 컨테이너가 prototype을 관리하는 범위

  1. 스프링 빈 생성
  2. 의존관계 주입(DI)
  3. 초기화 작업

—> 초기화 작업후 관리하지 않기 때문에 소멸자 실행이 되지 않음

프로토타입 빈의 특징

  • 스프링 컨테이너에 요청할 때마다 새로 생성됨
  • 프로토타입 빈의 생성 및 주입, 초기화 까지만 관여
  • 종료 메서드가 호출되지 않음 → 사용자가 직접 종료 메서드 호출 해야함

3. 싱글톤 안에 프로토 타입이 있는경우

  • Singleton Bean 내부의 Prototype Bean이 있는 Test에서 오류 발생
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(2); //오류발생!!
        /* Singleton내부에 있는 Prototype Bean은 최초 의존관계 주입 시점에 딱 1번 수행됨
           그 이후에 호출 할때는 Prototype Bean이 생성되지 않고 처음 주입된 PrototypeBean객체가
           호출되는 것 이므로 값이 1이 아닌 2인 것이다. */
    }
//Prototype Bean을 가지는 Singleton Bean 생성
    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;
        }
    }
//프로토 타입 Bean 객체
    @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");
        }
    }
}

💡 오류 발생 이유?

  • Singleton Bean이 생성되는 최초의 시점에 Prototype Bean이 생성된다. 그 후 호출되는 모든 객체는 똑같은 Prototype Bean을 참조(Singleton)해서 계속 누적해서 count 값이 더해지므로 오류가 발생하는 것이다.

오류 해결 → Provider의 사용

  • Dependency Lookup(DL) : 의존 관계 조회(탐색), 직접 필요한 의존관계를 찾는 것
  • 싱글톤 내부에 프로토타입 빈이 있을 때 호출할 때마다 프로토타입 빈이 새로운 값을 할당 받는 방법 : 호출할 때마다 bean을 찾아서 만들어 준다.
    → 원하는 프로토 타입 빈만 가져오기 위해 DL을 사용

Provider의 종류

1. ObjectiveProvider (구 ObjectFactory)

  • ObejctProvider의 getObject() 를 호출시, 스프링 컨테이너를 통해 해당 빈을 찾아서 반환(DL)

ObjectProvider의 특징

  • ObjectFactory를 상속 받아서 옵션, 스트림 처리등 편의기능
  • 별도 라이브러리 필요 없음
  • 스프링에 의존
@Scope("singleton") //prototype Bean을 가지는 Singleton Bean 생성
    static class ClientBean {

        @Autowired
        //Object Provider를 이용해서 DL 구현
        private Provider<PrototypeBean> prototypeBeanProvider;

        //getObject 메서드로 새로운 prototype Bean을 할당
        public int logic() {
            PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
            prototypeBean.addCount();
            int count = prototypeBean.getCount();
            return count;
        }
    }

2. JSR-330 Provider

  • 자바 표준에서 제공하는 Provider이다.
  • 별도의 라이브러리를 추가해야 한다. : javax.inject:javax.inject:1 를 gradle에 추가
  • provider.get() 메서드를 통해서 새로운 프로토타입 빈 생성

JSR-330 Provider의 특징
- get() 메서드 하나로 기능이 매우 단순
- 별도의 라이브러리가 필요함
- 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용 가능

@Scope("singleton") //prototype Bean을 가지는 Singleton Bean 생성
    static class ClientBean {

        @Autowired
        //JSR-330 Provider를 이용해서 DL 구현
        private Provider<PrototypeBean> prototypeBeanProvider;

        //get 메서드로 새로운 prototype Bean을 할당
        public int logic() {
            PrototypeBean prototypeBean = prototypeBeanProvider.get();
            prototypeBean.addCount();
            int count = prototypeBean.getCount();
            return count;
        }
    }

요약

  • 기본적으로 스프링에 내장된 기능인 ObjectProvider를 사용하자!
  • 다른 컨테이너에서도 사용해야 한다면 JSR-330Provider를 사용할 것
반응형
'Back-End/Spring' 카테고리의 다른 글
  • Jackson 라이브러리 - ObjectMapper, Object를 json으로 파싱하기
  • [Spring] 빈 생명주기 콜백
  • [Spring] 의존관계 주입 방법
  • [Spring]컴포넌트 스캔, 컴포넌트 스캔 필터 by 인프런 김영한님
LightSource
LightSource
어제보단 발전한 오늘의 나를 위한 블로그
    반응형
  • LightSource
    LightSourceCoder
    LightSource
  • 전체
    오늘
    어제
    • 분류 전체보기 (152)
      • Git (4)
      • Language (6)
        • Java (6)
      • Back-End (63)
        • Spring Boot (4)
        • MyBatis (1)
        • Oracle (1)
        • PL SQL (3)
        • JPA (26)
        • Spring Data JPA (5)
        • Spring MVC (8)
        • Spring (12)
        • Spring Security (2)
        • Redis (1)
      • Front-End (38)
        • 아이오닉 (2)
        • JSP (7)
        • JavaScript (4)
        • React (16)
        • TypeScript (3)
        • Angular (6)
      • AWS (1)
      • CI & CD (1)
      • 개발지식 (13)
        • 네트워크 (9)
        • CS 지식 (4)
      • 데이터모델링 (2)
      • Tool (1)
      • 프로젝트 (5)
      • 독후감 (2)
      • 잡생각 (0)
      • 면접 준비 (1)
      • 알고리즘 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    배열요소수정
    react
    배요소열추가
    리액트
    배열요소삭제
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
LightSource
빈 스코프(Bean Scope)와 프로토타입 스코프(Prototype Scope), 싱글톤에서의 프로토타입 스코프의 처리
상단으로

티스토리툴바