1 根據url獲取HandlerExecutionChain
從debug的堆棧圖中可以看到HandlerExecutionChain的結構:
(1)url對應的controller bean
(2)url對應的method
(3)所有該請求url攔截規則的intercepter
2 獲取處理器
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
3 遍歷攔截器並調用攔截器的preHandle
// Apply preHandle methods of registered interceptors.
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
4 利用java 反射調用controller的實際方法,其中數據綁定部分的代碼,在controller中可使用@InitBinder
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// RequestMappingHandlerAdapter
protected final ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
return invokeHandleMethod(request, response, handlerMethod);
...
}
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
// 在controller中用@InitBinder就是在這裏面起作用,比如對String類型進行html編碼
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
...
requestMappingMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
...
}
// InvocableHandlerMethod
private Object invoke(Object... args) throws Exception{
ReflectionUtils.makeAccessible(getBridgedMethod());
...
return getBridgedMethod().invoke(getBean(), args);
...
}
5 調用攔截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
6 處理異常和結果,在controller中可以定義@ExceptionHandler({BindException.class,WebException.class, ConstraintViolationException.class, ValidationException.class})註解進行異常處理。
(1)異常處理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception){
...
mv = processHandlerException(request, response, handler, exception);
...
}
(2)返回結果處理,最後調用的是javax包的forward、include、sendRedirect等等方法
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, request, response);
}
// 最後調用的是javax包的forward、include等等方法
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine which request handle to expose to the RequestDispatcher.
HttpServletRequest requestToExpose = getRequestToExpose(request);
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, requestToExpose);
// Expose helpers as request attributes, if any.
exposeHelpers(requestToExpose);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(requestToExpose, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(requestToExpose, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(requestToExpose, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.forward(requestToExpose, response);
}
}
7 在finally塊中執行攔截器的applyAfterConcurrentHandlingStarted方法
if (asyncManager.isConcurrentHandlingStarted()){
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}