빈 스코프 - 프로토타입 스코프

2023. 8. 26. 16:46Spring/[inflearn]스프링 핵심 원리 - 기본편

◎ 프로토타입 스코프

-> 프로토타입 스코프는 스프링 컨테이너에 조회하면 스프링 컨테이너는 항상 새로운 인스턴스를 생성해서 반환한다.

프로토타입 빈 요청1

1. 프로토타입 스코프의 빈을 스프링 컨테이너에 요청한다.

2. 스프링 컨테이너는 이 때 프로토타입 빈을 생성하고 의존관계를 주입한다.

 

프로토타입 빈 요청2

3. 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에게 반환한다.

4. 모두 반환하면 스프링 컨테이너는 프로토타입 빈을 더 이상 관리하지 않고, 이후 스프링 컨테이너에 같은 요청이 오면 프로토타입 빈을 새로 생성하여 반환한다.

 

※ 중요 포인트

- 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계를 주입하고 초기화까지만 처리한다.

- 클라이언트에 반환한 후에는 더 이상 프로토타입 빈을 관리하지 않는다.

- 프로토타입 빈을 관리할 책임은 프로토타입 빈을 반환받은 클라이언트에게 있다. 그래서 @PreDestroy 같은 종료 메서드가 호출되지 않는다.

 

◎ 싱글톤 스코프와 프로토타입 스코프 비교

-> 싱글톤 스코프 테스트

import org.assertj.core.api.Assertions;
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;

public class SingletonTest {
    @Test
    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);

        Assertions.assertThat(singletonBean1).isSameAs(singletonBean2);

        ac.close(); // 종료

    }

    @Scope("singleton") // 싱긍톤 스코프 선언(singleton이 기본값)
    static class SingletonBean {
        @PostConstruct
        public void init(){
            System.out.println("SingletonBean.init");
        }

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

-> 결과

- singletonBean1과 singletonBean2는 모두 같은 인스턴스 빈을 조회하였다.

- 이후 종료 메서드까지 실행되었다.

 

-> 프로토타입 스코프 테스트

import org.assertj.core.api.Assertions;
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;

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);

        Assertions.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");
        }
    }
}

-> 결과

- 싱글톤 빈은 스프링 컨테이너 생성 시점에 초기화 메서드가 실행되지만, 프로토타입 스코프 빈은 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드도 생성된다.

- prototypeBean1과 prototypeBean2는 서로 다른 스프링 빈이다. 초기화(PrototypeBean.init())이 2번 실행되었다.

- 싱글톤 빈은 스프링 컨테이너가 관리하여 스프링 컨테이너가 종료될 때 빈의 종료 메서드(destroy())가 실행되지만, 프로토타입 빈은 스프링 컨테이너가 생성, 의존관계 주입, 초기화까지만 관여하기 때문에 초기화 이후는 스프링 컨테이너가 관여하지 않는다.  따라서 스프링 컨테이너가 종료될 떄 @PreDestroy()같은 종료 메서드가 실행되지 않는다.

- 프로토타입 빈에서 종료 메서드를 호출하려면 클라이언트가 직접 호출해야 한다.

 

-> 프로토타입 스코프에서 종료 메서드 직접 호출

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);

        Assertions.assertThat(prototypeBean1).isNotSameAs(prototypeBean2);

        /*
         프로토타입 빈은 초기화 이후는 클라이언트가 직접 관리해야 한다.
         따라서 초기화 이후의 종료 메서드를 직접 호출한다.
         */
        prototypeBean1.destroy();
        prototypeBean2.destroy();

        ac.close(); // 종료
    }

-> 결과

 

 

☆ 참고

[인프런]스프링 핵심 원리 - 기본편