在之前的分析中我们分析了DispatcherServlet类中doDispatcher方法内调用的以下方法中的1-5
本文主要分析方法6,分析SpringMvc是处理分发结果的。
在该源码中需要关注以下代码
1.getHandler(processedRequest) 获取处理器链的方法
2.getHandlerAdapter(mappedHandler.getHandler()) 获取处理器链适配器
3.mappedHandler.applyPreHandle(processedRequest, response) 请求前预处理
4.ha.handle(processedRequest, response, mappedHandler.getHandler())调用适配器的处理方法
5.mappedHandler.applyPostHandle(processedRequest, response, mv)请求执行后处理
6.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
以下是processDispatchResult方法的源码:
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);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//重点关注此处,绘制视图即页面渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
//执行拦截器列表里的afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
在上面的源码中可以看到render(mv, request, response)方法,它的作用是绘制视图
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
//通过视图名来获取视图
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
//判断视图是否存在
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//视图渲染
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}