인프런 강의 < 스프링 핵심 원리 - 기본편 > 정리
스코프는 빈이 존재할 수 있는 범위를 말한다.
1. 싱글톤 스코프의 빈을 스프링 컨테이너에 요청한다.
2. 스프링 컨테이너는 본인이 관리하는 스프링 빈을 반환한다.
3. 이후에 스프링 컨테이너에 같은 요청이 와도 같은 객체 인스턴스의 스프링 빈을 반환한다.

1. 프로토타입 스코프의 빈을 스프링 컨테이너에 요청한다.
2. 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 필요한 의존관계를 주입한다.

3. 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환한다.
4. 이후에 스프링 컨테이너에 같은 요청이 오면 항상 새로운 프로토타입 빈을 생성해서 반환한다.
@PreDestory같은 종료메서드는 호출되지 않는다.public class SingletonTest {
@Test
void singletonBeanFind() {
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")
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.PrototypeTest$SingletonBean@54504ecd
singletonBean2 = hello.core.scope.PrototypeTest$SingletonBean@54504ecd
org.springframework.context.annotation.AnnotationConfigApplicationContext -
Closing SingletonBean.destroy
public class PrototypeTest {
@Test
void prototypeBeanFind() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
System.out.println("find prototypeBean1");
PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
System.out.println("find prototypeBean2");
PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
System.out.println("prototypeBean1" + prototypeBean1);
System.out.println("prototypeBean2" + prototypeBean2);
assertThat(prototypeBean1).isNotSameAs(prototypeBean2);
}
@Scope("prototype")
static class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
}
find prototypeBean1
PrototypeBean.init
find prototypeBean2
PrototypeBean.init
prototypeBean1 = hello.core.scope.PrototypeTest$PrototypeBean@13d4992d
prototypeBean2 = hello.core.scope.PrototypeTest$PrototypeBean@302f7971
org.springframework.context.annotation.AnnotationConfigApplicationContext -
Closing
PreDestory 같은 종료 메서드가 실행 되지 않음!!스프링 컨테이너에 프로로타입 스코프의 빈을 요청하면 항상 새로운 객체를 반한한다.
- 하지만 싱글톤 빈과 함께 사용할 때는 원하는대로 동작하지 않는다.

clientBean은 싱글톤이므로, 보통 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생clientBean은 의존관계 자동 주입을 사용한다. 주입 시점에 스프링 컨테이너에 프로토타입 빈을 요청clienBean에 반환한다. 프로토타입 빈의 conunt 필드 값은 0이다.clientBean은 프로토타입 빈을 내부 필드에 보관한다.( 정확히 말하면 참조값을 보관한다.)
clientbean을 스프링 컨테이너에 요청해서 받는다. 싱글톤이므로 항상 같은 clientBean이 반환된다.clientBean.logic()을 호출한다.clientBean은 prototypeBean의 addCount()를 호출해서 프로토타입 빈의 count를 증가시킨다.
clientbean을 스프링 컨테이너에 요청해서 받는다. 싱글톤이므로 항상 같은 clientBean이 반환된다.clientBean.logic()을 호출한다.@Autowired
private ApplicationContext ac;
public int logic() {
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
ObjectProvider이다. 참고로 과거에는 ObjectFactory가 있었고 여기에 편의 기능을 추가한것이 ObjectProvider이다.static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
ObjectProvider의 getObject()를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.Dependency Lookup 정도의 기능만 제공javax.inject.Provider라는 JSR-330 자바 표준을 사용하는 방법javax.inject:javax.inject:1을 추가해야 한다.@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
Provider.get()을 통해 새로운 프로토타입 빈 생성get()하나라서 기능이 매우 단순
[d06b992f...] request scope bean create
[d06b992f...][http://localhost:8080/log-demo] controller test
[d06b992f...][http://localhost:8080/log-demo] service id = testId
[d06b992f...] request scope bean close
- 포멧: [UUID][requestURL]{message}
@Component
@Scope(value = "request")
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message) {
System.out.println("[" + uuid + "]" + "[" + requestURL + "]" + message);
}
@PostConstruct
public void init() {
uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "] request scope bean create:" + this );
}
@PreDestroy
public void close() {
System.out.println("[" + uuid + "] request scope bean close:" + this );
}
}
MyLogger 클래스@Scope(value = "request"): request 스코프 지정@PostConstruct 초기화 메서드를 사용해서 uuid 생성해 저장함.@PreDestroy를 사용해서 종료 메시지 남김requestURL은 이 빈이 생성되는 시점에 알수 없으므로. 외부에서 setter로 입력받음@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final MyLogger myLogger;
@RequestMapping("log-demo")
@ResponseBody // 뷰화면이 없고 바로 문자열 반환
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURI().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("Service id = " + id);
}
}
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final ObjectProvider<MyLogger> myLoggerProvider;
@RequestMapping("log-demo")
@ResponseBody // 뷰화면이 없고 바로 문자열 반환
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURI().toString();
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("Service id = " + id);
}
}
ObjectProvider 덕분에 getObject()를 호출하는 시점까지 request scope 빈의 생성 지연 가능@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
}
proxyMode = ScopedProxyMode.TARGET_CLASS를 추가한다
- 적용 대상이 인터페이스가 아닌 클래스면 TARGET_CLASS
- 적용 대상이 인터페이스면 INTERFACES 선택
System.out.println("myLogger = " + myLogger.getClass());
myLogger = class hello.core.common.MyLogger$$EnhancerBySpringCGLIB$$b68b726d

다형성)< 자료 출처: 스프링 핵심 원리 - 기본편 >