BACK/SPRING

[Spring] 스프링 핵심 원리 - 빈 스코프

연듀 2024. 3. 4. 21:16

 

스코프: 빈이 존재할 수 있는 범위

 

싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프

 

프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입(초기화 메서드까지)

까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프

클라이언트에게 빈을 반환하고, 스프링 컨테이너는 이후에 빈을 관리하지 않음(이후에는 클라이언트가 관리)

 

  • 컨테이너에 요청할 때마다 새로 생성
  • 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드도 실행
  • 종료 메서드가 호출되지 않음 (클라이언트가 직접 해야함)

 

프로토 타입 스코프, 싱글톤 빈 함께 사용 시 문제점

 

public class SingletonWithPrototypeTest1 {
    @Test
    void prototypeFind(){ // 스프링 컨테이너에 프로토타입 빈 직접 요청
   		// 요청 할 때마다 새롤운 빈을 생성하기 때문에 조회한 두개의 프로토타입 빈의 count는 각각 1이다. 
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean2.class);
        PrototypeBean2 prototypeBean = ac.getBean(PrototypeBean2.class);
        prototypeBean.addCount();
        assertThat(prototypeBean.getCount()).isEqualTo(1);

        PrototypeBean2 prototypeBean2 = ac.getBean(PrototypeBean2.class);
        prototypeBean2.addCount();
        assertThat(prototypeBean2.getCount()).isEqualTo(1);
    }

    @Test
    void singletonClientUsePrototype(){
     // 싱글톤 빈은 생성 시점에만 의존관계 주입을 받기 때문에 처음 생성된 프로토타입이 계속 유지된다. 
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean2.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);

    }

    @Scope("singleton")
    static class ClientBean{ // 싱글톤에서 프로토타입 빈 사용
    	// ClientBean 은 싱글톤이므로 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생한다. 
        
        private final PrototypeBean2 prototypeBean2; // 의존관계 주입시 프로토타입빈을 요청하고 스프링 컨테이너가 프로토타입 빈을 생성해 반환한다. // 
        // 생성 시점에 주입하고, 싱글톤이므로 항상 같은 clientBea이 반환되고 prototypeBean도 계속 같은걸 사용

        public ClientBean(PrototypeBean2 prototypeBean2) { // 프로토타입 빈 달라고 컨테이너에 요청 -> 만들어 줌 
            this.prototypeBean2 = prototypeBean2;
        }

        public int logic(){
            prototypeBean2.addCount();
            return prototypeBean2.getCount();
        }
    }

    @Scope("prototype")
    static class PrototypeBean2{ // 프로토타입 빈 
        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");
        }
    }
}

 

 

지정한 프로토타입 빈을 컨테이너에서 대신 찾아주는 DL 기능을 사용해 이 문제를 해결한다.

Dependency Lookup: 의존관계 조회(탐색)

 

ObjectProvider

@Autowired
 private ObjectProvider<PrototypeBean> prototypeBeanProvider; // 직접 등록하지 않아도
	// 스프링에서 자동으로 만들어 등록
 public int logic() {
     PrototypeBean prototypeBean = prototypeBeanProvider.getObject(); // 항상
	// 새로운 프로토타입 빈 생성하게 해줌
     prototypeBean.addCount();
     int count = prototypeBean.getCount();
     return count;
}

 

ObjectProvider의 getObject() 를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.

기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워진다.

 

 

 

웹 관련 스코프

 

request: 웹 요청이 들어오고 나갈때 까지 유지되는 스코프

  • 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성,관리

 

session: 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프

  • HTTP session 과 동일한 생명주기 가짐

 

application: 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프

 

 

 

 

 

참고) 스프링 핵심 원리 기본편

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#