DispatcherServlet
์ Servlet
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ, HTTP ํ๋กํ ์ฝ๋ก ๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ ๋จผ์ ๋ฐ์ ์ฒ๋ฆฌํ๋ Front Controller ์ด๋ค.
Servlet ์ด๋?
Java ์ธ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก, ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ด๋ HTTP ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ์๋ตํ๋ ์ญํ ์ ๋ด๋นํ๋ ์ธํฐํ์ด์ค ์ด๋ค.
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
์์ ๊ฐ์ ๋ฉ์๋๋ค์ด ์ ์๋์ด ์๋ค.
์ค์ํ ๋ถ๋ถ์ service(ServletRequest req, ServletResponse res)
์ธ๋ฐ, ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ฐ์ ์ฒ๋ฆฌํ๊ณ ์๋ต์ ์์ฑํ๋ ํต์ฌ ๋ฉ์๋์ด๋ค.
DispatcherServlet
์ ์ด Servlet
์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์ด๊ณ , ๊ฒฐ๊ณผ์ ์ผ๋ก service()
๋ฉ์๋๋ฅผ ๊ตฌํํ ํน์ ๋ฉ์๋๋ฅผ ์คํ์์ผ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด๋ค.

์์ ๊ฐ์ ์์ ๊ตฌ์กฐ๋ฅผ ๊ฐ๊ณ ์๊ณ , DispatcherServlet
์ด ์ด๋ป๊ฒ ์ฌ์ฉ์์ ์๋ต์ ์ฒ๋ฆฌํ๋์ง ์ดํด๋ณด์.
HttpServlet | service()
service()
๋ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋๋ผ๊ณ ํ๋ค.
Servlet
ํด๋์ค๋ฅผ ์์ํ๋ ํด๋์ค ์ค, HttpServlet
์ด ํด๋น ๋ฉ์๋๋ฅผ Override ํ๋ค.
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
service(request, response);
}
์ญํ ์, ServletRequest
๋ฅผ HttpServletRequest
๋ก ๋ณํํ๊ณ ServletResponse
๋ฅผ HttpServletResponse
๋ก ๋ณํํ ๋ค,
service(HttpServletRequest req, HttpServletResponse res)
๋ก์ service()
๋ฉ์๋๋ฅผ Overload(Overrideโ) ํ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์ด Overload ํ ๋ฉ์๋๋ ํ์ ํด๋์ค์ธ FrameWorkServlet
ํด๋์ค์์ ์ฐพ์ ์ ์๋ค.
FrameWorkServlet | service()
@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);
}
}
// private static final Set<String> HTTP_SERVLET_METHODS =
// Set.of("DELETE", "HEAD", "GET", "OPTIONS", "POST", "PUT", "TRACE");
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ๋๋ถ๋ถ์ HTTP ์์ฒญ์ GET, POST, PUT, DELETE ์์ฒญ์ด๋ฏ๋ก
์ ๋ก์ง์ ๋ฐ๋ผ, super.service(request,response)
๊ฐ ํธ์ถ๋์ด FrameworkServlet
์ ์์ ํด๋์ค์ service()
๋ฉ์๋๊ฐ ํธ์ถ๋ ๊ฒ์ด๋ค.
๋ค์ HttpServlet
ํด๋์ค์ service(HttpServletRequest req, HttpServletResponse res)
๋ฉ์๋๊ฐ Overload ๋์ด ๊ตฌํ๋์ด์์ผ๋ฏ๋ก ๋์์๋ค.
HttpServlet | service()
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
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 {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
์์ ๊ฐ์ ๋ฉ์๋๋ก ๊ตฌํ๋์ด ์๋ค.
๊ฐ HTTP ๋ฉ์๋์ ๋ง๊ฒ doXX ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ๋ ๋ก์ง์ด ์งํ๋๋ค.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
์ ๋ฉ์๋๋ฅผ ๋ฐ๊ฒฌํ ์ ์์๋๋ฐ, protected
ํค์๋๋ฅผ ๋ณด๋ ํ์ ํด๋์ค์์ ์๋กญ๊ฒ ์ ์ํ ๋ฉ์๋๊ฐ ์๊ฒ ๊ตฌ๋ ์๊ฐ์ด ๋ค์ด์ ํ์ ํด๋์ค๋ฅผ ํ์ํด๋ณด์๋ค.
HttpServlet
์ ์์ํ FrameWorkServlet
ํด๋์ค์์ Override ํ doXX ๋ฉ์๋๊ฐ ์์์ ํ์ธํ๋ค.
FrameWorkServlet | doGet()
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
์์ ๊ฐ์ ๋ฉ์๋๋ก ์ ์๋์ด ์๋๋ฐ, processRequest(request, response)
๋ผ๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ๋ ๋์ด์๋ค.
FrameWorkServlet | processRequest()
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
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);
try {
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new ServletException("Request processing failed: " + ex, ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
์ด๋ฌํ ๋ฉ์๋๊ฐ ์์๋ค.
๊ฒฐ๋ก ์ doService(request, response)
๋ฅผ ํธ์ถํ๋ค.
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
๊ทผ๋ฐ, doService()
๋ฉ์๋๋ ์ถ์ ๋ฉ์๋์๋ค.
์ฆ, ์ถ์ ๋ฉ์๋๋ก ์ญํ ์ ์ ์ํด๋๊ณ ํ์ ํด๋์ค์์ ๊ตฌํํ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ฒ๋ ํ๋ ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ด ์ ์ฉ๋์ด ์์๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ํ์ ํด๋์ค์ธ DispatcherServlet
์์ ๊ตฌํํ doService(HttpServletRequest request, HttpServletResponse response)
๊ฐ ํธ์ถ๋๋ค.
DispatcherServlet | doService()
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.
.
.
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
DispatcherServlet
์ doService()
๋ฉ์๋๋ ๋ค์, doDispatch()
๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
DispatcherServlet | doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new ServletException("Handler dispatch failed: " + err, err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new ServletException("Handler processing failed: " + err, err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch()
๋ฉ์๋๋ ์์ ๊ฐ๋ค.
mappedHandler = getHandler(processedRequest)
๋ถ๋ถ์์ ์์ฒญ url์ ๊ธฐ๋ฐ์ผ๋ก ์ ์ ํ ํธ๋ค๋ฌ(Controller)๋ฅผ ์ฐพ๋๋ค.
๊ทธ๋ฆฌ๊ณ HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())
๋ถ๋ถ์์ ์ฐพ์ ํธ๋ค๋ฌ๋ฅผ ์คํํ๊ธฐ ์ํ ํธ๋ค๋ฌ Adapter๋ฅผ ์ฐพ๋๋ค.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler())
๋ถ๋ถ์์ ํธ๋ค๋ฌ ์ด๋ํฐ๋ฅผ ์คํํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ Controller ๋ก์ง์ ์คํํ๊ฒ ๋๋ค.
์ฐธ๊ณ ํ ๋ธ๋ก๊ทธ : https://zzang9ha.tistory.com/441