[Spring] mvc - DispatcherServlet 1부
서블릿과 스프링 MVC는 밀접하면서 독립적이다.
DispatcherServlet 클래스는 HTTP 요청을 처리하는 중심 디스패처 클래스이다.
DispatcherServlet 클래스는 아래와 같은 계층 구조를 가진다.
Servlet 인터페이스
서블릿의 핵심은 요청과 응답이다. ServletRequest과 ServletResponse가 요청 및 응답을 추상화해놓은 인터페이스이다.
public interface Servlet {
// ..
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
HttpServlet 추상 클래스
Servlet 인터페이스의 구현체인 HttpServlet 라는 추상클래스가 있다. 이 클래스는 Servlet 인터페이스가 정의한 service 메서드를 HTTP 메서드 별로 호출하도록 분기를 해놓았다.
public abstract class HttpServlet extends GenericServlet {
// ..
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
// ..
doGet(req, resp);
}
} else if (method.equals(METHOD_HEAD)) {
// ..
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
// ..
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
스프링이 아닌 서블릿을 직접 구현을 해보았다면 익히 보았을 doGet, doHead, doPost, doPut, doDelete 등의 protected 메서드들이 있다. 이 메서드들을 보통 오버라이딩해서 구현하는 코드를 짰을 것이다.
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// ..
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
FrameworkServlet 추상클래스
스프링에서는 FrameworkServlet가 HttpServlet 추상클래스의 doXXX 메서드들을 오버라이드한다.
doGet, doHead, doPost, doPut, doDelete 등은 모두 processRequest 라는 내부의 메서드를 호출하게 된다.
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
// ..
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ..
try {
doService(request, response);
}
// ..
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
processRequest에서 핵습은 doService를 호출하는 것이다.
재미있는 것은 Servlet 인터페이스의 핵심은 원래 service 하나였다.
그런데 HttpServlet 추상클래스가 쪼개어 놓은 각 메서드를 스프링이 다시 하나로 합치고 있는 것이다.
DispatcherServlet 클래스
DispatcherServlet는 FrameworkServlet 추상클래스에서 추상메서드로 정의한 doService 메서드를 구현을 하고 있다.
DispatcherServlet은 doService를 doDispatch를 호출한다.
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ..
try {
doDispatch(request, response);
// ..
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ..
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// ..
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
doDispatch에서 핵심로직은 HandlerAdapter를 가져와서 handle 메서드를 호출하는 일이다.
HandlerAdapter 인터페이스는 아래와 같이 간단하다.
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
handle 메서드는 AbstractHandlerMethodAdapter에서 오버라이드 하고 있는데 handleInternal 메서드를 호출하도록 되어 있다.
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
구현체인 RequestMappingHandlerAdapter 클래스의 handleInternal 는 invokeHandlerMethod 를 호출한다.
invokeHandlerMethod 내부에서는 ServletInvocableHandlerMethod의 invokeAndHandle을 호출하게 된다.
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// ..
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
try {
// ..
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// ..
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// ..
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
// ..
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
ServletInvocableHandlerMethod 의 계층구조는 아래와 같다.
ServletInvocableHandlerMethod 클래스에서 invokeAndHandle 메서드에서는 핵심은 invokeForRequest 및 HandlerMethodReturnValueHandlerComposite의 handleReturnValue의 호출이다.
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// ..
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
InvocableHandlerMethod 클래스에 invokeForRequest 메서드가 구현이 되어 있다. 내부의 doInvoke 메서드가 호출된다.
public class InvocableHandlerMethod extends HandlerMethod {
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
return doInvoke(args);
}
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
getBridgedMethod 메서드는 HandlerMethod 클래스에 구현이 되어 있는데 아래와 같다.
public class HandlerMethod {
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
private HandlerMethod resolvedFromHandlerMethod;
protected Method getBridgedMethod() {
return this.bridgedMethod;
}
bridgedMethod 가 호출하는 메서드가 컨트롤러의 요청을 처리하는 메서드이다.
만약 아래와 같은 컨트롤러가 있다고 하면 home() 같은 메서드가 bridgedMethod 가 될 수 있을 것이다.
인자가 있었다면 parameters에 해당 인자들이 배열로 구성이 된다.
@Controller
public class JspViewTestController {
@RequestMapping(value = "/")
public String home() {
return "index";
}
invoke는 말그대로 Controller의 메서드가 호출되는 것이다.
메서드가 리턴이 되면 invokeAndHandle의 invokeForRequest 호출이 반환되고 결과를 처리하는 handleReturnValue 가 호출이 된다.
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// ..
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
HandlerMethodReturnValueHandlerComposite 는 HandlerMethodReturnValueHandler 인터페이스를 구현하고 있는 컴포지트 클래스인데 내부에 HandlerMethodReturnValueHandler 들의 리스트를 가지고 있다.
리턴타입을 지원하는 핸들러를 찾아서 해당 핸들러를 적용할 수 있게 해준다.
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
HandlerMethodReturnValueHandler 인터페이스를 구현한 처리기는 아래 목록 처럼 꽤 많다.
등록되는 핸들러만 15개이다.
위의 내용을 도식화하면 아래와 같다.