public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
public class DispatcherServlet extends FrameworkServlet {
//...
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);
// 여기서 핸들러를 조회
// 핸들러 없으면 NoHandlerFoundException을 터트리거나, 404 Error를 보낸다.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 아래 내부 코드 확인!!!
// Object > HandlerAdapter 리턴
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
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;
}
}
// 아래 내부 코드 확인!!!
// 등록된 인터셉터가 있으면 순서대로 preHandle 메소드가 실행
// preHandle 메소드에서 false 처리되어도 postHandle은 실행되지 않지만 내부적으로 triggerAfterCompletion가 실행된다.
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 핸들러 호출하여 컨트롤러 실행
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 이레 부분은 비동기 요청 처리를 위해 사용된다.
// true면 비동기 모드이므로, 동기 방식으로 처리하지 않도록 실행을 중단시킨다.
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// modelAndView 에 View 등록
applyDefaultViewName(processedRequest, mv);
// 아래 내부 코드 확인!!!
// 인터셉터 postHandle 메소드 호출
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);
}
// 예외나 에러가 발생해도 afterCompletion 호출
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()) {
// 아래 내부 코드 확인!!!
// 비동기 처리일 경우 후처리 메소드 호출
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 멀티파트 요청일 경우 리소스 해제(임시 파일 삭제, 메모리 정리) 작업 수행
// 멀티파트 요청: 보통 파일 업로드와 같은 작업
// 헤더에 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXYZ 형태로 포함되어있다.
// boundary로 여러 파트로 구분된다.
// 폼 데이터와 파일 데이터(바이너리 데이터)를 모두 포함할 수 있다.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
//...
}
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");
}
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;
}
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);
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// 예외가 있을 경우 처리
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 예외가 없는 경우 ModelAndView 렌더링
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
// 비동기일 경우 처리 마무리
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// afterCompletion 호출
if (mappedHandler != null) {
// Exception (if any) is already handled..
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);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
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);
}
}
}
}
}
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
default void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
}
- 비동기 요청 들어오면 preHandle
- 비동기 처리 시작 afterConcurrentHandlingStarted
- 요청 완료 후 afterCompletion
Reference: