Singleton
: Spring Container에서 사용되는 가장 기본 Scope로 Container시작과 부터 종료시점까지 유지된다Prototype
: Spring Container가 빈의 생성과 주입만 담당하는 Scope 그이후 부터는 클라이언트가 관리 하게 된다(Singleton과는 반대로 호출시에 항상 다른 인스턴스가 필요할때 사용한다.) Request
: 사용자의 Request요청이 들어오고 Response 될때까지 유지되는 ScopeSession
: Session이 생성되고 종료될 때까지의 ScopeScope
애노테이션을 이용한다.수동주입시
@Configuration
public class Config {
@Bean
@Scope("prototype")
TestBean testBean(){
return new TestBean();
}
}
자동주입시
@Component
@Scope("prototype")
public class TestBean {
...
}
Prototype Scope Bean을 Singleton Scope Bean이 주입받아서 사용할 때 Prototype은 Container가 Prototype Bean를 Singleton Bean를 호출시 마다 계속 새로 주입 해주는 것이 아니라 처음에 Singleton bean을 생성할 때 final로 생성해 주었던 Prototype Bean이 실행된다.
따라서 생성과 주입 까지는 Container가 관리하나 이후는 Client인 Singleton Bean 이 관리하기 되므로 Singleton Bean에 주입된 Prototype Bean은 마치 Singleton Bean처럼 사용된다.
public class SingletonWithPrototype {
@Test
public void SingletonClientUsePrototype() throws Exception {
//given
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
//when
ClientBean bean1 = ac.getBean(ClientBean.class);
ClientBean bean2 = ac.getBean(ClientBean.class);
//then
assertThat(bean1.logic()).isEqualTo(1);
assertThat(bean2.logic()).isEqualTo(2);
assertThat(bean1.prototype).isSameAs(bean2.prototype);
//PreDestroy 확인용
ac.close();
}
/**
* 테스트 결과 : 성공
* 우리가 원하는 의도는 bean1의 prototype과 bean2의 prototype과 서로 다른 인스턴스여야 하지만
* 이 테스트에서는 같은 인스턴스로 확인 되어 우리가 원하는 결과와는 다름.
* */
@Scope("singleton")
@RequiredArgsConstructor
static class ClientBean {
private final PrototypeBean prototype;
public int logic() {
prototype.addCount();
return prototype.getCount();
}
}
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public int getCount() {
return count;
}
public void addCount() {
count++;
}
@PostConstruct
public void init() {
System.out.println("init = " + this);
}
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
}
}
private final ApplicationContext ac;
public int logic() {
PrototypeBean prototype = ac.getBean(PrototypeBean.class);
prototype.addCount();
return prototype.getCount();
}
public class SingletonWithPrototype {
@Test
public void SingletonClientUsePrototypeFind() throws Exception {
//given
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
//when
ClientBean bean1 = ac.getBean(ClientBean.class);
ClientBean bean2 = ac.getBean(ClientBean.class);
//then
assertThat(bean1.logic()).isEqualTo(1);
assertThat(bean2.logic()).isEqualTo(1);
}
/**
* 테스트 결과 : 성공
* 우리가 원하는 의도대로 서로 다른 Prototype Bean을 가지고 logic을 실행한 것을 볼 수 있다.
* */
@Scope("singleton")
@RequiredArgsConstructor
static class ClientBean {
/**
* ObjectProvider 스프링에 의존적
* DL 기능을 해주는 객체 getObject호출시 원하는 bean을 찾아줌
*/
private final ObjectProvider<PrototypeBean> beanProvider;
public int logic() {
PrototypeBean prototype = beanProvider.getObject();
prototype.addCount();
return prototype.getCount();
}
}
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public int getCount() {
return count;
}
public void addCount() {
count++;
}
@PostConstruct
public void init() {
System.out.println("init = " + this);
}
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
}
}
private final ObjectProvider<PrototypeBean> beanProvider;
ObjectProvider는 ObjectFactory의 Stream,Optinal등 여러가지 편의 기능들을 추가한 인터페이스로 DL 기능을 보다 쉽고 편하게 해준다.javax.inject.Provider
를 사용해도 괜찮다.출처 : 스프링 핵심 원리