ServletContainer와 SpringContainer는 무엇이 다른가?
서블릿 컨테이너는 오직 서블릿 API 만 지원하는 것을 말한다 (JSP, JSTL 까지 포함해서)
애플리케이션 서버는 Java EE (EJB, JMS, CDI, JTA, 서블릿 API) 의 전체를 지원한다
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
서블릿이 GET , POST 요청을 처리할 수 있도록 서버가 서비스 메소드를 호출합니다. 해당 메소드를 overriding 하여 다양한 form의 데이터를 다루고 반환할 수 있다.
Front Controller Design Pattern - GeeksforGeeks
application의 모든 요청은 앞단의 handler에 의해 처리되며 해당 handler에서 적절한 다음 handler로 요청이 발송된다.
- 서블릿 컨테이너의 제일 앞에서 모든 요청을 받아 처리( 세부 controller 로 요청을 위임)
- 프론트 컨트롤러를 제외한 나머지 컨트롤러들은 서블릿을 사용하지 않아도 됨
- 장점 : 한 곳에서 사용자의 모든 요청을 다룰수 있기 때문에 공통 패턴에 대한 적용이 중복적으로 사용되지 않을 수 있다.
서블릿 별로 WebApplciationContext 인스턴스를 관리한다. 서블릿의 구성은 서블릿의 네임 스페이스에 있는 빈에 의해 결정된다. 요청이 성공적으로 처리되었는지에 관계 없이 요청 처리시 이벤트를 게시합니다.
DispatcherServlet에 대한 작업만 수행 , HttpServletRequest 에 대한 공통 작업
attribute setting
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// attribute setting
try {
doDispatch(request, response); // 내부적으로 doDispatch 호출
} 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);
}
}
}
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); // 적절한 Handler(Controller)를 찾음
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.
// resource cache 여부 확인 , 동일한 request 재요청시에 대한 선 처리
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;
}
}
// intercepter의 선 처리 ( 통과할 경우 true , 다음 작업 실행 )
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// view 를 찾고 model을 매핑 , @RestController일 경우 null이기 때문에 실행 x
applyDefaultViewName(processedRequest, mv);
// interceptor 의 후 처리
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 NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", 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);
}
}
}
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;
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter: this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
ModelAndView object with the name of the view and the required model data, or null if the request has been handled directly
doDispatch()
4 : getHandler() : HandlerMapping
5 : getHandlerApdater() : Handler 를 처리할 수 있는 HandlerAdapter를 찾는다.
Intercepteer preHandle
6 : handle() : HandlerApdater에서 해당 컨트롤러를 통해 요청 처리
Intercepter postHandle
7 : applyDefaultViewName :
8 : processDispatchResult :
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// Did the handler return a view to render?
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(); // view 의 논리 이름
if (viewName != null) {
// view의 논리 이름 ViewResolver에 전달하여 물리 이름으로 변환 , View 객체를 반환받는다.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 반환받은 view 객체에 model(data)를 매핑
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 이름을 바탕으로 viewResolver를 통해 view 객체를 반환
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
// doDispatch()
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
//applyAfterConcurrentHandlingStarted()
void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (interceptor instanceof AsyncHandlerInterceptor) {
try {
AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptor;
asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
} catch (Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error("Interceptor [" + interceptor + "] failed in afterConcurrentHandlingStarted", ex);
}
}
}
}
}