目录
1.概述
视图解析是SpringMVC响应HTTP请求的最后一步,即将handler处理结果转换为对应的视图返回给客户端,而所谓的视图可以是模板,如freemarker,也可以是HTML或者JSP等。本文主要剖析视图解析的时机,有哪些视图解析器,重点介绍一两个解析器。
2.视图解析的时机
这里直接抛出答案:在统一异常处理结束后进行视图解析(因为异常处理handler也可能返回视图)。关于统一异常处理,见: SpringMVC统一异常处理源码解析.
下面直接看代码,在DispatcherServlet中,获取到handler的返回结果后交给processDispatchResult方法来处理,而在这个方法里面包含两个逻辑:1.异常处理 2.视图处理
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//1.异常处理
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);
}
}
// 2.视图处理
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
//-- 日志 --
}
//异步请求的开始
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
//handler处理完成回调
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
可以看到render方法是处理视图的核心,接下来看render方法的实现。
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) {
//通过视图名称找到视图并转换为View对象
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 {
//如果handler方法返回里面没有视图名称,则尝试直接获取view对象,如果没有则报错
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() + "'");
}
}
//-- 省略日志 ----
try {
//设置resp状态
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
//---日志------
throw ex;
}
}