서블릿과 스프링 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개이다.
위의 내용을 도식화하면 아래와 같다.
'Programing > Framework' 카테고리의 다른 글
[spring boot] 2.1.6 -> 2.2.0 테스트 깨짐(인코딩) (0) | 2020.02.18 |
---|---|
[spring integration] TCP 연결시간 설정 (0) | 2020.02.18 |
[Spring] SSE vs WebFlux (0) | 2020.02.08 |
[Spring] mvc 예외처리 (0) | 2020.02.07 |
[Spring] ServiceLocatorFactoryBean (0) | 2020.01.31 |