SpringMVC中视图解析器源码分析
该篇博客只分析视图解析器源码,如果想看DispatcherServlet的具体流程,请看SpringMVC中DispatcherServlet源码
直接进入视图解析的入口,如下
进入该方法
关键的一行代码如上,进入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) {
// 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.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
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 + "]", ex);
}
throw ex;
}
}
其中关键的一行如下(变量view是View类型的视图)
mv是一个ModelAndView对象,ModelAndView对象中存储了一个ModelMap,其中mv.getModelIntenal()方法就是返回这个ModelMap,该map存储了一些我们自己定义的属性,我们平时在controller层的方法传一个Map参数应该就是这个map(这是我的猜想,继续往后看你就知道为何我会这么想了)
进入该方法。
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
关键的一行如下
从名字我们可以看出,该类是想合并一些东西。点进这个方法,实现类是InternalResourceView
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, 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(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
上面这行代码就是将之前Map中的值全部放入Request中,进入该方法,结果如下
如上图:map中的值全部会放到request域中
这下应该知道为什么我们在controller层的方法中传入map后在jsp中用request取出来吧。(所以之前的猜想应该是正确的)
最后将我们的视图显示出来。如上,如果视图是jsp,会跳转到jsp对应的servlet将视图发送给浏览器(jsp的本质还是servlet)
当然这只是视图解析的大致流程,具体细节的话可以自己查看源码。如果有误,欢迎指出。
其它说明:以上是springmvc-5.0.2版本的源码,不同版本源码可能有差异