Bean Scope ‐ rquest scope - Hot-stock/backend GitHub Wiki
Request Scope와 Spring에서의 동작 원리
Spring MVC에서 Request Scope는 **HTTP 요청(Request)**이 들어오는 동안에만 살아있는 객체를 의미합니다. 이를 이해하기 위해 먼저 서블릿(Servlet)과 Spring MVC의 내부 동작 방식을 살펴보겠습니다.
Request Scope란 Spring MVC에서 유지되는 방법
Request Scope는 ThreadLocal을 통해서 값이 유지됩니다. 이 ThreadLocal 객체를 조회하는 것이 RequestContextHolder입니다. RequestContextHolder가 ThreadLocal을 관리하는 방법을 보면 서블릿의 service()
를 호출하게 되면 다음과 같은 흐름으로 진행됩니다.
- DispatcherServlet -> FrameworkServlet -> process() -> ThreadLocal 관리
RequestContextHolder를 보면 ThreadLocal이 static으로 선언되어 있습니다. 그 후 processRequest()
를 통해 요청을 처리하면서 ThreadLocal에 값을 넣어줍니다.
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (HTTP_SERVLET_METHODS.contains(request.getMethod())) {
super.service(request, response);
} else {
processRequest(request, response);
}
}
}
processRequest()
중 일부
초기화 부분: RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
Request Scope와 Proxy 객체 사용
우리는 생성자 주입 시점을 사용하기 때문에 Request Scope의 주입 시점이 굉장히 애매해집니다. 이를 해결하기 위해 스프링에서는 proxy 객체를 사용하는 방법을 사용합니다. 이 때문에 호출 시점에서 proxy 객체를 호출하면, proxy 객체는 RequestContextHolder를 사용해 값이 없으면 새로운 요청을 생성하여 ThreadLocal에 값을 삽입합니다. 이후, 우리에게는 진짜 객체처럼 동작합니다.
Proxy 객체 예제 코드
@Override
public void setUserId(String userId) {
// 1. RequestContextHolder에서 실제 객체 조회
UserContext actualContext = (UserContext) RequestContextHolder
.getRequestAttributes()
.getAttribute("scopedTarget.userContext", RequestAttributes.SCOPE_REQUEST);
// 2. 객체가 없으면 생성
if (actualContext == null) {
actualContext = new UserContext();
RequestContextHolder.getRequestAttributes()
.setAttribute("scopedTarget.userContext", actualContext, RequestAttributes.SCOPE_REQUEST);
}
// 3. 실제 객체에 메서드 호출 위임
actualContext.setUserId(userId);
}