Bean scope에 대한 글을 작성 중에, 아래와 같은 사실을 알게 되었습니다.
Container에 Bean이 생성될 때 Bean의 의존성은 조립이 완성된 채로 생성됩니다.
즉, Singleton Bean이 생성될 때 Prototype Bean도 생성이 되어 의존성 주입이 이뤄지는데요.
결국 Prototype Bean 입장에서 클라이언트인 Singleton Bean을 위해 Container 내부에 하나의 인스턴스가 생성이 되고, 그 인스턴스가 Singleton Bean에 의존됩니다.따라서 Singleton Bean이 가지는 Prototype Bean에게 요청을 해도, 결국 하나의 인스턴스를 사용하는 셈입니다.
그런데 문득, 그러면 정말 애플리케이션을 실행시킬 때마다 다른 인스턴스가 생성되나? 라는 의문이 들었고 이를 직접 확인하고 싶었습니다.
우선 기본적으로 프로토타입 객체를 만들고, 프로토타입 객체를 의존하는 싱글톤 객체를 만들었습니다.
@Component
@Scope(value = "prototype")
public class PrototypeObject {
}
@Component
@Getter
@AllArgsConstructor
public class SingletonObject {
private final PrototypeObject prototypeObject;
}
그 후 테스트 코드로 제대로 구현했는지 확인해보았습니다.
@SpringBootTest
public class PrototypeTest {
private final ApplicationContext context = new AnnotationConfigApplicationContext(TestApplication.class);
@Test
@DisplayName("Container에 올라간 프로토타입 빈이 프로토타입 빈인지 확인한다.")
void isPrototypeObjectPrototypeBean() {
boolean isPrototype = context.isPrototype("prototypeObject");
boolean isSingleton = context.isSingleton("prototypeObject");
assertThat(isPrototype).isTrue();
assertThat(isSingleton).isFalse();
}
@Test
@DisplayName("ApplicationContext에서 프로토타입 빈을 2번 요청하고 이 둘을 비교한다")
void differentInstancePrototypeBean() {
PrototypeObject prototypeObject1 = context.getBean("prototypeObject", PrototypeObject.class);
PrototypeObject prototypeObject2 = context.getBean("prototypeObject", PrototypeObject.class);
assertThat(prototypeObject1).isNotEqualTo(prototypeObject2);
}
@Test
@DisplayName("두 개의 다른 타입의 싱글톤 객체 내에 프로토타입 객체가 있을 때 이 두 프로토타입 객체를 비교한다")
void showPrototypeScope() {
SingletonObject singletonObject = context.getBean("singletonObject", SingletonObject.class);
OtherSingletonObject otherSingletonObject = context.getBean("otherSingletonObject", OtherSingletonObject.class);
assertThat(singletonObject.getPrototypeObject()).isNotEqualTo(otherSingletonObject.getPrototypeObject());
}
}
이제 진짜 애플리케이션을 매번 실행할 때마다 다른 인스턴스를 생성하는지 확인해보겠습니다.
테스트 코드로는 애플리케이션을 다시 실행시키는 방법이 있는지 모르기에, 직접 컨트롤러를 구현하고 포스트맨으로 요청을 보내 결과를 확인하는 식으로 확인했습니다.
@Controller
@AllArgsConstructor
public class TestController {
private final SingletonObject singletonObject;
@GetMapping("/show")
@ResponseBody
public String show() {
return "singletonObject = " + singletonObject.toString() + "\n" +
"protoTypeObject = " + singletonObject.getPrototypeObject().toString();
}
}
첫 번째 시도입니다.
두 번째 시도입니다.
싱글톤 빈도 다른 인스턴스가 생성되고, 그에 따른 프로토타입 빈도 다른 인스턴스가 생성됩니다.
결과를 보고나니 어쩌면 당연한 결과라고 느껴지네요.