Dispatcher Servlet
DispatcherServlet
์ด๋ Presentation Layer ์ ๋ฉด์์ ๋ชจ๋ HTTP ์์ฒญ์ ์ค์์ง์ค์์ผ๋ก ์ฒ๋ฆฌํ๋ Front Controller ํจํด์ ์ ์ฉํ Servlet
์ด๋ค.
ServletContainer
๊ฐ ๋ฐ์ ํด๋ผ์ด์ธํธ ์์ฒญ์ ๊ณตํต์ ์ธ ์์
์ DispatcherServlet
์ด ์ฒ๋ฆฌํ๊ณ ๊ทธ ์ธ ์์
์ ์ ์ ํ Controller ์๊ฒ ์์ํ๋ค.
์ฆ, Controller ๋ก ํฅํ๋ ๋ชจ๋ ์น ์์ฒญ์ ์ง์ ์ ์ผ๋ก์จ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ตํ๋ค.
Front Controller ํจํด
Front Controller ํจํด์ ๋ชจ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ํ๋์ ๋ํ Controller ๋ฅผ ๋ฐฐ์นํ๋ ํจํด์ ์๋ฏธํ๋ค.
Front Controller ๋ ๋ชจ๋ ์์ฒญ์ ์ง์
์ ์ด ๋์ด ์์ฒญ์ ์ผ๊ด์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค. Spring ์์๋ DispatcherServlet
์ด Front Controller ์ญํ ์ ํ๋ค.
Dispatcher Servlet ๋์ ๊ณผ์
ServletContainer
๊ฐ ๋ฐ์ ์์ฒญ์ดHttpServletRequest
๋ก ๊ฐ๊ณต๋์ดDispatcherServlet
์๊ฒ ์ ๋ฌ๋๋ค. ์ด๋MultipartResolver
LocaleResolver
ThemeResolver
์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๊ฐ ์์ฒญ์ ๋ถ์ํ๋ค.- ์์ฒญ์
HandlerMapping
์๊ฒ ์์ํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌํ Handler(Controller) ๋ฅผ ํ์ํ๋ค. HandlerAdapter
์์ Handler ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋HandlerAdapter
๋ฅผ ํ์ํ๋ค.HandlerAdapter
์ ์ ์๋ Handler ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ๋ค.- Handler ๊ฐ
viewName
ํน์ModelAndView
๋ฅผ ๋ฐํํ๋ค. ViewResolver
์๊ฒviewName
ํน์ModelAndView
๋ฅผ ์ ๋ฌํ์ฌ ํด๋นํ๋View
๋ฐํํ๋ค.DispatcherServlet
์View
์๊ฒModel
์ ์ ๊ณตํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ตํ ํ๋ฉด์ ์์ฑํ๋ค.DispatcherServlet
์View
์ ๊ฒฐ๊ณผ๋ฅผHttpServletResponse
๊ฐ์ฒด๋ฅผ ํตํด ํด๋ผ์ด์ธํธ์๊ฒ ์๋ตํ๋ค.
์ฝ๋ ์์์ ์ดํด๋ณด๊ธฐ
// DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
doDispatch(request, response);
}
// ...
}
ServletContainer
๊ฐ HttpServletRequest
์ HttpServletResponse
๋ฅผ ์์ฑํ์ฌ DispatcherServlet
์๊ฒ ์ ๋ฌํ ๋ doService()
๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
์ด๋ Handler ๋ฅผ ์ฐพ๊ณ ์์ฒญ์ ์์ํ๋ ๋ฑ์ ์ญํ ์ ํ๋ doDispatch()
๋ฉ์๋๋ก ์ฐ๊ฒฐ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
mappedHandler = getHandler(processedRequest);
}
}
// ...
}
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
๋จผ์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ Handler ๋ฅผ ์ฐพ๊ธฐ ์ํด getHandler()
๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
}
// ...
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
// ...
}
์ฐพ์ Handler ๋ฅผ ์คํํ๊ธฐ ์ํ HandlerAdapter
๋ฅผ ์ฐพ๊ธฐ ์ํ getHandlerAdapter()
๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
}
}
// ...
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
Handler ๋ฅผ ์คํํ๊ธฐ ์ applyPrehandle()
๋ฉ์๋๋ฅผ ํตํด Interceptor
์ preHandle()
๋ฉ์๋๋ฅผ ์คํํ๋ค. ์ด๋ HandlerInterceptor
๋ฅผ ๊ตฌํํ ๋น์ด ํธ์ถ๋๋ค. ์ฃผ๋ก ์ธ์ฆ์ ์ํด ๋ง์ด ์ฌ์ฉํ๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...
}
// HandlerAdapter.java
public interface HandlerAdapter {
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
Interceptor
๋ฅผ ํต๊ณผํ ํ Handler ์ ๋ฉ์๋๋ฅผ ์คํํ๊ธฐ ์ํด HandlerAdapter
์ handle()
๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
}
// ...
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
Handler ๊ฐ ์คํ๋ ์ดํ Interceptor
์ postHandle()
์ด ์คํ๋๋ค. ์ด๋ Handler ๊ฐ ๋ฐํํ ModelAndView
์ ๋ํ ํ์ฒ๋ฆฌ๋ฅผ applyPostHandle()
๋ฉ์๋๊ฐ ์ํํ๋๋ฐ, @Controller
์ ๊ฒฝ์ฐ ModelAndView
๋ฅผ ๋ฐํํ๊ณ @RestController
์ ๊ฒฝ์ฐ null
์ ๋ฐํํ์ฌ View
๊ฐ ๋ ๋๋ง๋๋ ๊ณผ์ ์ ์๋ตํ๊ฒ ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// ...
}
private void processDispatchResult(
HttpServletRequest request,
HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler,
@Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// ...
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// ...
}
protected void render(
ModelAndView mv,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// ...
View view;
String viewName = mv.getViewName();
if (viewName != null) {
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// ...
}
// ...
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
// ...
}
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
// View.java
public interface View {
void render(
@Nullable Map<String, ?> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
์ฒ๋ฆฌ๋ ModelAndView
๋ processDispatchResult()
๋ผ๋ ๋ฉ์๋๋ฅผ ํตํด ๋ ๋๋ง๋๋ค.
ํด๋น ๋ฉ์๋ ๋ด๋ถ์์ render()
๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ฐ ์ด๋ viewName
์ View
๊ฐ์ฒด๋ก ๋ณํํ๋ resolveViewName()
๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
์ดํ ๋ฐํ๋ฐ์ View
๊ฐ์ฒด์ render()
๋ฉ์๋ ํธ์ถ์ ํตํด View
์ Model
์ ๋ ๋๋งํ๋ ๊ณผ์ ์ ๊ฑฐ์น๊ฒ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
// ...
}
}
๋ง์ง๋ง์ผ๋ก Interceptor
์ triggerAfterCompletion()
๋ฉ์๋๋ฅผ ์คํํ๋ฉด ์์ฒญ์ ๋ํ ์ฒ๋ฆฌ๊ฐ ์ ๋ถ ๋ง๋ฌด๋ฆฌ๋๋ค.
์ ๋ฆฌ
DispatcherServlet
์ ๊ฐ๋จํ ์์๋ณด๊ณ ์ค์ ๋ด๋ถ ๋์ ๊ณผ์ ์ ์ดํด๋ดค๋ค. ์ ์ฒด์ ์ธ ํ๋ฆ์ ์ ๋ฆฌํ๋ฉด ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
References
- https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet-context-hierarchy
- https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-servlet
- https://tecoble.techcourse.co.kr/post/2021-06-25-dispatcherservlet-part-1/
- https://tecoble.techcourse.co.kr/post/2021-07-15-dispatcherservlet-part-2/