본문 바로가기
혼자 공부하는 것들/Spring

Spring 빈 스코프를 알아보자♻️

by applepick 2022. 1. 31.
반응형

빈 스코프는 말 그대로 스프링 빈이 존재할 수 있는 범위를 말합니다. 스프링은 아래와 같은 스코프를 지원합니다.

  • 싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프입니다.
  • 프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프입니다.
  • 웹 관련 스코프
    request: 웹 요청이 들어오고 나갈때 까지 유지되는 스코프입니다.
    session: 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프입니다.
    application: 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프입니다.

프로토타입 스코프의 빈을 스프링 컨테이너에 요청합니다. 그 뒤에 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 필요한 의존관계를 주입합니다.

이후 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환합니다. 앞으로 스프링 컨테이너에 같은 요청이 오면 항상 새로운 프로토타입 빈을 생성해서 반환합니다.

 

싱글톤 빈과 차이점은 싱글톤은 스프링 DI 컨테이너에 요청이오면 같은 인스턴스의 빈을 반환해주지만, 프로토타입은 요청 시 항상 새로운 프로토타입을 반환해주는 차이점을 가지고 있습니다. 

 

여기서 핵심은 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리한다는 것입니다. 클라이언트에 빈을 반환하고, 이후 스프링 컨테이너는 생성된 프로토타입 빈을 관리하지 않습니다. 프로토타입 빈을 관리할 책임은 프로토타입 빈을 받은 클라이언트에 있습니다. 예를 들어 클라이언트에서 프로토타입의 빈을 요청하면, 스프링 DI 컨테이너는 해당 프로토타입 빈을 반환 즉시 관여하지 않습니다. 만약 종료 메서드가 필요하면 클라이언트에서 직접 종료 메서드를 사용해야합니다.  스프링 DI 컨테이너 안에서 @PreDestroy 같은 종료 메서드가 호출되지 않습니다. 


테스트 코드 작성

싱글톤 테스트

import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import static org.assertj.core.api.Assertions.assertThat;

public class SingletonTest {
    @Test
    public void singletonBeanFind() {
        AnnotationConfigApplicationContext ac = new
                AnnotationConfigApplicationContext(SingletonBean.class);
        SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        System.out.println("singletonBean1 = " + singletonBean1);
        System.out.println("singletonBean2 = " + singletonBean2);
        assertThat(singletonBean1).isSameAs(singletonBean2);
        ac.close(); //종료
        }

        @Scope("singleton")
        static class SingletonBean {
            @PostConstruct
            public void init() {
                System.out.println("SingletonBean.init");
            }
            @PreDestroy
            public void destroy() {
                System.out.println("SingletonBean.destroy");
            }
        }
}

위의 테스트 결과

SingletonBean.init
singletonBean1 = hello.core.scope.SingletonTest$SingletonBean@6cb107fd
singletonBean2 = hello.core.scope.SingletonTest$SingletonBean@6cb107fd
SingletonBean.destroy

싱글톤은 하나의 빈으로 2번 호출되는 것을 확인해볼 수 있습니다. init 메서드와 destroy 메서드가 한번만 호출되는 모습을 확인할 수 있습니다. 

 

프로토타입 테스트

import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import static org.assertj.core.api.Assertions.assertThat;

public class PrototypeTest {
    @Test
    public 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(); //종료

    }
    @Scope("prototype")
    static class PrototypeBean {
        @PostConstruct
        public void init() {
            System.out.println("PrototypeBean.init");
        }
        @PreDestroy
        public void destroy() {
            System.out.println("PrototypeBean.destroy");
        }
    }
}

위 프로토타입 빈 테스트 결과

find prototypeBean1
PrototypeBean.init
find prototypeBean2
PrototypeBean.init
prototypeBean1 = hello.core.scope.PrototypeTest$PrototypeBean@24c22fe
prototypeBean2 = hello.core.scope.PrototypeTest$PrototypeBean@93081b6

각 각 요청에 따라 새로운 빈을 생성하는 것을 볼수있습니다. 하나 특이한 점은 init 메서드는 각 호출마다 2번 생성되는 모습을 볼 수 있지만, distory의 요청은 허용 하지 않았습니다. 위에서 말했다시피, 프로토타입 스코프는 스프링 DI 컨테이너 안에서 종료 메서드가 호출되지 않습니다. 만약 컨테이너 안에서 종료 메서드를 호출하고 싶다면, 아래와 같이 종료 메서드를 직접 명시해서 종료해 줄 수는 있습니다. 하지만 잘 쓰이지는 않습니다. 

prototypeBean1.destroy();

 

반응형

댓글