Bean은 Spring IoC 컨테이너(그 중에서도 주요 인터페이스인 Bean Factory)에 의해 인스턴스화, 생성, 관리된다. 객체의 인스턴스화, 생성, 관리는 configuration metadata에 명시된 대로 이루어진다.
그 configuration metadata에 해당하는 것들은 아래와 같다.
그 중 해당 포스트에서는 Bean Scope에 대해 알아보고자 한다.
특정 Bean의 configuration metadata로 Bean Scope를 지정하면 Java의 클래스 수준에서 객체의 생성, 소멸을 관리하지 않아도 된다. Spring Framework는 5개의 Bean Scope를 지원하고 있다.
해당 Scope로 생성된 Bean은 단 하나의 공유되는 Bean으로 관리된다. IoC Container에 단 하나의 객체만 존재한다. 해당 Scope는 Spring의 default scope라는 특징이 있다.
public class Single { }
@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
private final ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("single");
System.out.println(ctx.getBean(Single.class);
System.out.println(ctx.getBean(Single.class);
}
}
위의 Single class는 따로 Bean Scope를 지정하지 않고 있으므로 default 값인 Singleton Scope가 적용된다. 따라서 Single class는 IoC 컨테이너 내에 단 하나만 존재하게끔 관리되어 하단의 코드인 getBean(Single.class);를 호출했을 때 항상 동일한 Bean을 리턴한다.
실행 결과는 아래와 같다.
매번 요청을 받을 때마다 새로운 Bean을 생성하는 Bean Scope이다. 다른 Bean에 주입될 때(런타임 시 x) 혹은 getBean() 메소드가 호출될 때마다 새로운 Bean이 생성된다.
한 가지 주의할 점은 Spring이 Prototype Scope를 갖는 Bean의 경우 완벽한 lifecycle을 관리해주지 않는다는 것이다.
client에서 Prototype Scope를 갖는 Bean을 호출하면 인스턴스화하고 configuration metadata를 참조하여 Bean을 구성한 뒤 client 단에 넘겨준다. 하지만 해당 Bean을 제거하는 것까진 Spring이 관리하지 않는다는 의미이다. Spring은 오로지 Bean 객체의 initialization 과정에만 관여하고 destruction에 관여하지 않는다. 따라서 Prototype Scope를 가진 Bean을 사용하는 client에서 해당 Bean을 제거하는 로직을 따로 작성해두어야 한다.
@Component @Scope(value = "prototype",
proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Proto { }
@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
private final ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("proto");
System.out.println(ctx.getBean(Proto.class);
System.out.println(ctx.getBean(Proto.class);
}
}
Proto class는 Prototype Scope로 지정된 Bean이므로 getBean() 메소드를 호출할 때마다 다른 객체를 생성하고 있음을 코드를 통해서도 확인할 수 있었다.
web-base인 애플리케이션에서만 사용 가능한 Scope이다. XmlBeanFactory나 ClassPathXmlApplicationContext와 같이 web-base가 아닌 container에서 사용할 경우 IllegalStateException을 얻게 된다.
해당 Scope를 갖는 Bean은 HTTP 요청이 올 때마다 객체를 새로 생성한다. 2시 27분에 도달한 request에서 Request Scope의 Bean을 수정했다고 가정한다. 이후 2시 30분에 도달한 또 다른 request 객체에서 동일한 Request Scope의 Bean을 사용한다고 할 때 27분에 수정된 Bean이 아닌 원본의 Bean을 사용하게 된다. Request Scope으로 생성된 Bean은 각 Request마다 따로 생성되기 때문이다. Request 처리가 끝나면 Request Scope를 갖는 Bean은 함께 소멸된다.
Request Scope를 갖는 Bean과 동일하게 web-base인 애플리케이션에서만 사용 가능한 Scope이다.
해당 Scope를 갖는 Bean은 하나의 새로운 HTTP Session이 생성될 때마다 객체를 새로 생성한다. Request Scope처럼 Session Scope를 갖는 Bean을 수정하더라도 다른 HTTP Session을 갖는 Session Scope Bean은 영향을 받지 않는다는 특징이 있다. 또한 HTTP Session이 만료되면 Session Scope Bean도 함께 소멸된다.
Global Session Scope는 portlet-base인 애플리케이션에서만 사용 가능한 Scope이다. 하지만 앞선 Request Scope와 Session Scope와 달리 portlet-base인 애플리케이션이 아닌 servlet-base인 애플리케이션에서 사용하게 되면 IllegalStateException와 같은 에러가 발생하지 않고 Session Scope로 설정된다는 특징이 있다.
해당 Scope를 갖는 Bean 역시 Global Session이 만료되면 Global Session Scope Bean도 함께 소멸된다.
[참조] https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch04s04.html