스프링/핵심원리+MVC

스프링 - 빈 스코프

이째형 2021. 7. 27. 16:52

빈 스코프란?

빈 스코프는, 빈이 존재할 수 있는 범위를 의미한다.

빈 스코프의 종류

1. 싱글톤 타입

스프링 컨테이너의 시작부터 종료까지 유지되는 가장 넓은 범위의 스코프. 스프링의 기본 스코프이다.

2. 프로토 타입

스프링 컨테이너가 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프이다.

3. 웹 관련 스코프

  • request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
  • session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
  • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

빈 스코프의 지정 (@Scope 이용)

- 컴포넌트 스캔을 이용한 등록

@Scope("prototype")
@Component
public class HelloBean {
/
...
/
}

 

- 수동 등록

@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
   return new HelloBean();
}

프로토 타입 스코프

싱글톤 타입 스코프의 빈을 조회하면 스프링은 항상 같은 인스턴스의 빈을 반환하지만, 프로토 타입 스코프의 빈을 조회하면 스프링 컨테이너는 항상 새로운 인스턴스를 생성해서 반환한다.

 

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

 

클라이언트에 빈을 반환하고, 이후 스프링 컨테이너는 생성된 프로토타입 빈을 관리하지 않는다.

프로토타입 빈을 관리할 책임은 프로토타입 빈을 받은 클라이언트에 있다. 그래서 @PreDestroy 같은 종료 메서드가 호출되지 않는다. (클라이언트가 직접 종료해줘야한다.)

 

프로토 타입 스코프를 싱글톤 타입과 같이 사용할 때의 문제점

clientBean이라는 싱글톤 빈이 프로토 타입 빈을 DI 받아서 사용하는 예시를 들어보자.

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

클라이언트 빈은 싱글톤이므로 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입이 발생한다.

주입 시점에 클라이언트 빈은 스프링 컨테이너에 프로토 타입 빈을 요청한다.

스프링 컨테이너는 프로토 타입 빈을 생성해서 클라이언트 빈에 반환한다. 이 때 프로토타입 빈의 카운트 값은 0이다.

클라이언트 빈은 이제 프로토타입 빈의 참조 값을 내부 필드에 보관한다.

 

클라이언트 A가 클라이언트 빈을 스프링 컨테이너에 요청한다. 클라이언트 빈은 싱글톤이므로 항상 같은 인스턴스가 반환된다. 클라이언트 빈은 프로토타입 내부의 애드 카운트를 호출해서 count값이 1 증가한다.

여기서 클라이언트 B가 나타나서 스프링 컨테이너에게 클라이언트 빈을 요청한다. 이 때 문제가 발생한다. 클라이언트 빈 내부에 있는 프로토타입 빈은 이미 DI시점에 생성이 끝난 것이고, 새로 생성되지 않는다. 이로 인해서 count가 0에서 시작하는 것이 아닌 1에서 시작해서 1->2가 되는 문제가 발생한다.

 

스프링은 일반적으로 싱글톤 빈을 사용하므로, 싱글톤 빈이 프로토타입 빈을 사용하게 된다. 그런데 싱글톤 빈은 생성 시점에만 의존관계 주입을 받기 때문에, 프로토타입 빈이 새로 생성되기는 하지만, 싱글톤 빈과 함께 계속 유지되는 것이 문제다.