해당 내용은 '스프링 입문을 위한 자바 객체 지향의 원리와 이해'와 인프런 김영한님의 '스프링 핵심 원리 - 기본편' 강의를 참고하였습니다.
스프링 빈은 스프링 컨테이너의 시작과 함께 생성되고, 스프링 컨테이너가 종료될 때까지 유지된다.
이와 같이 스프링 빈이 존재할 수 있는 범위를 의미하는 것을 우리는 스코프 라고 한다.
스프링은 다음과 같은 스코프를 지원한다.
우리가 여태까지 사용한 빈들은 전부 싱글톤 스코프이니, 굳이 싱글톤 스코프의 사용법에 대해서는 다루지 않고, 바로 프로토타입 스코프부터 알아보자.
보통 외부 클라이언트가 싱글톤 빈을 요청하면 스프링 컨테이너에서는 하나의 싱글톤 빈만 생성하고 같은 요청에 대해 같은 객체의 인스턴스로 스프링 빈을 반환하지만,
보통 우리가 아는 싱글톤 스코프 타입의 빈은 다음과 같이 스프링 컨테이너에 의해 관리된다.
프로토 타입의 빈 요청은 다르다.
자 여기서 중요한건 프로토타입 빈은 스프링 컨테이너에서 빈 생성, DI, 초기화까지만 하고 반환하며, 그 뒤로는 관리하지 않는다는 것이다.
프로토타입 빈을 관리할 책임은 해당 빈을 요청한 클라이언트에게 있다.
그렇기에 우리가 이전에 공부했던 @PreDestroy
와 같은 종료 메서드가 호출되지 않는다.
그럼 이제 코드로 알아보자.
우선 싱글톤 빈부터 확인해보자.
SingletonTest.java
public class SingletonTest {
@Test
void singletonBeanFine(){
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") // default 라서 사실 안해도됨..
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@71a6dcb7
singletonBean2 = hello.core.scope.SingletonTest$SingletonBean@71a6dcb7
SingletonBean.destroy
init 과 destroy가 모두 호출되었고, 두 번의 요청에 같은 빈을 반환한 것을 알 수 있다.
이번에는 프로토타입 빈 스코프를 사용해보자.
PrototypeTest.java
import static org.assertj.core.api.Assertions.assertThat;
public class PrototypeTest {
@Test
void PrototypeBeanFind(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
System.out.println("find prototypeBean 1");
PrototypeBean PrototypeBean1 = ac.getBean(PrototypeBean.class);
System.out.println("find prototypeBean 2");
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");
}
}
}
이번에는 두 반환 빈이 다를 것이니 isNotSameAs
를 사용했다.
결과는 다음과 같다.
find prototypeBean 1
PrototypeBean.init
find prototypeBean 2
PrototypeBean.init
PrototypeBean1 = hello.core.scope.PrototypeTest$PrototypeBean@2b7eccc4
PrototypeBean2 = hello.core.scope.PrototypeTest$PrototypeBean@f83d00bf
자 init이 2번 호출되었고, destroy는 단 한번도 호출되지 않았으며,
생성된 2개의 프로토타입 빈의 객체가 다른 것을 알 수 있다.