spring mvc DispatcherServlet详解之四---视图渲染过程

时间:2023-02-12 12:57:22

整个spring mvc的架构如下图所示:

spring mvc DispatcherServlet详解之四---视图渲染过程

现在来讲解DispatcherServletDispatcherServlet的最后一步:视图渲染。视图渲染的过程是在获取到ModelAndView后的过程。

视图渲染的过程:

DispatcherServlet.java

doService()--->doDispatch()--->processDispatchResult()--->render()

processDispatchResult():主要处理异常、请求状态及触发请求完成事件,图的渲染工作交给了render().

render()渲染过程如下:

1. 判断ModelAndView中view是否为view name,没有获取其实例对象:如果是根据name,如果是则需要调用resolveViewName从视图解析器获取对应的视图(View)对象;否则ModelAndView中使用getview方法获取view对象。

2. 然后调用view的render()方法。

具体代码如下:

/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
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.resolveLocale(request);
response.setLocale(locale); View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), 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 {
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;
}
}

那么view 是如何渲染的?我们来看看view的定义:

org.springframework.web.servlet
Interface View All Known Subinterfaces:
SmartView
All Known Implementing Classes:
AbstractAtomFeedView, AbstractExcelView, AbstractFeedView, AbstractJasperReportsSingleFormatView, AbstractJasperReportsView, AbstractJExcelView, AbstractPdfStamperView, AbstractPdfView, AbstractRssFeedView, AbstractTemplateView, AbstractUrlBasedView, AbstractView, ConfigurableJasperReportsView, FreeMarkerView, InternalResourceView, JasperReportsCsvView, JasperReportsHtmlView, JasperReportsMultiFormatView, JasperReportsPdfView, JasperReportsXlsView, JstlView, MappingJackson2JsonView, MappingJacksonJsonView, MarshallingView, RedirectView, TilesView, TilesView, VelocityLayoutView, VelocityToolboxView, VelocityView, XsltView -------------------------------------------------------------------------------- public interface ViewMVC View for a web interaction. Implementations are responsible for rendering content, and exposing the model. A single view exposes multiple model attributes.
This class and the MVC approach associated with it is discussed in Chapter 12 of Expert One-On-One J2EE Design and Development by Rod Johnson (Wrox, 2002). View implementations may differ widely. An obvious implementation would be JSP-based. Other implementations might be XSLT-based, or use an HTML generation library. This interface is designed to avoid restricting the range of possible implementations. Views should be beans. They are likely to be instantiated as beans by a ViewResolver. As this interface is stateless, view implementations should be thread-safe.

spring提供了如此多的视图,那么肯定的是也会有很多视图解析器:

org.springframework.web.servlet
Interface ViewResolver All Known Implementing Classes:
AbstractCachingViewResolver, AbstractTemplateViewResolver, BeanNameViewResolver, ContentNegotiatingViewResolver, FreeMarkerViewResolver, InternalResourceViewResolver, JasperReportsViewResolver, ResourceBundleViewResolver, TilesViewResolver, TilesViewResolver, UrlBasedViewResolver, VelocityLayoutViewResolver, VelocityViewResolver, XmlViewResolver, XsltViewResolver
Functional Interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. -------------------------------------------------------------------------------- public interface ViewResolverInterface to be implemented by objects that can resolve views by name.
View state doesn't change during the running of the application, so implementations are free to cache views. Implementations are encouraged to support internationalization, i.e. localized view resolution.

其中,针对JSP提供的InternalResourceViewResolver与InternalResourceView。

我们先看一下view的render方法是什么样子的?

根据InternalResourceView的继承关系:

最终找到render方法在AbstractView中,如下代码所示:

    /**
* Prepares the view given the specified model, merging it with static
* attributes and a RequestContext attribute, if necessary.
* Delegates to renderMergedOutputModel for the actual rendering.
* @see #renderMergedOutputModel
*/
@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);
}

流程如下:

创建一个动态值和静态属性的map;

设置response 报文头;

把渲染view的工作放到renderMergedOutputModel()实现中,这个留给InternalResourceView来实现。

我们看看这个实现:

/**
* Render the internal resource given the specified model.
* This includes setting the model as request attributes.
*/
@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);
}
}

流程可以归纳为以下几步:

1. 包装request,供RequestDispatcher来使用;

2. 将map中的属性和值作为属性放入包装的request;

3. 将不同实现类的helper放入包装的request中;

4. 渲染前的准备,确定request dispatcher要跳向(或者inclue)的路径

5. 获取request dispatcher。

6. 根据request中是否包含include uri属性来确实是forward或者include方法。

forward是跳向服务器的servlet, JSP文件, 或者 HTML文件。

  Includes the content of a resource (servlet, JSP page,HTML file) in the response.

注意,在上述流程中出现了RequestDispatcher,那么这类的作用是什么呢?

getRequestDispatcher

RequestDispatcher getRequestDispatcher(java.lang.String path)
Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path. A RequestDispatcher object can be used to forward a request to the resource or to include the resource in a response. The resource can be dynamic or static.
The pathname specified may be relative, although it cannot extend outside the current servlet context. If the path begins with a "/" it is interpreted as relative to the current context root. This method returns null if the servlet container cannot return a RequestDispatcher. The difference between this method and ServletContext#getRequestDispatcher is that this method can take a relative path.

简洁的来说,

1. RequestDispatcher 是一个包装器,它将制定路径的(静态或者动态)资源包装起来。RequestDispatcher 可以用于将一个请求分发给指定的资源或者包裹响应报文中的资源。
2. RequestDispatcher 的获取,有这种形式,一种使用ServletRequest.getRequestDispatcher(java.lang.String path). 另一种是servletContext.getRequestDispatcher(java.lang.String path);不同之处在于:前面的方法支持相对路径,以'/'作为当前上下文的跟路径;后一种不支持后一种不支持相对路径。

小结:

可以看到视图的渲染过程是把model包装成map形式通过request的属性带到服务器端。

spring mvc DispatcherServlet详解之四---视图渲染过程的更多相关文章

  1. &lpar;转载&rpar;spring mvc DispatcherServlet详解之一---处理请求深入解析

    要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构: 从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServl ...

  2. spring mvc DispatcherServlet详解之interceptor和filter的区别

    首先我们看一下spring mvc Interceptor的功能及实现: http://wenku.baidu.com/link?url=Mw3GaUhCRMhUFjU8iIDhObQpDcbmmRy ...

  3. spring mvc DispatcherServlet详解之一---处理请求深入解析

    要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构: 从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServl ...

  4. spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程

    整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet通过request获取控制器Controller的过程,现在来讲解DispatcherServletDisp ...

  5. spring mvc DispatcherServlet详解之前传---前端控制器架构

    前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现 ...

  6. spring mvc DispatcherServlet详解之前传---FrameworkServlet

    做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationCont ...

  7. spring mvc DispatcherServlet详解之三---request通过ModelAndView中获取View实例的过程

    整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet第二步:通过request从Controller获取ModelAndView.现在来讲解第三步:reques ...

  8. spring mvc DispatcherServlet详解之一--request通过HandlerMaping获取控制器Controller过程

    整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的第一步:获取控制器. HandlerMapping HandlerMappi ...

  9. spring mvc DispatcherServlet详解之一---处理请求深入解析&lpar;续&rpar;

    上文中,我们知道分发过程有以下步骤: 分发过程如下: 1. 判断是否设置了multipart resolver,设置的话转换为multipart request,没有的话则继续下面的步骤. 2. 根据 ...

随机推荐

  1. &lbrack;WinForm&rsqb; DataGridView 绑定 DT &amp&semi;&amp&semi; ComboBox 列绑定 Dict

    一  需求介绍 一般像枚举类型的数据,我们在数据库里存储着诸如(1.2.3.4-)或者("001"."002"."003"-)此类,但是界面 ...

  2. 高度平衡的二叉搜索树&lpar;AVL树&rpar;

    AVL树的基本概念 AVL树是一种高度平衡的(height balanced)二叉搜索树:对每一个结点x,x的左子树与右子树的高度差(平衡因子)至多为1. 有人也许要问:为什么要有AVL树呢?它有什么 ...

  3. u盘安装ubuntu server 14&period;04 以及No CD-ROM drive was detected 错误

    u盘安装ubuntu server 14.04 1:下载ubuntu server14的 iso镜像文件 2:下载 UltraISO U盘镜像制作工具 : 3:使用Ultra iOS 将下载好的 is ...

  4. Exception与相关

    怎么写一个exception类, 直接抛出去,主要是写一个构造函数里面的Msg消息,这个可以提前写出来. try...catch..finally 一般都是一起的,try 中有异常执行语句, catc ...

  5. 【转】Linux 技巧&colon; Bash 参数和参数扩展

    重点看下清单7 现在,很多 Linux® 和 UNIX® 系统上都有 bash shell,它是 Linux 上常见的默认 shell.通过本文,您将了解到如何在 bash 脚本中处理参数和选项,以及 ...

  6. gem &&num;39&semi;logstash-devutils&&num;39&semi;

    需求 为了开发新的 logstash 插件 问题 原以为只是很简单的 bundle install 就能搞定的事情,实际却遇到了一堆问题. 1. clone git git@github.com:lo ...

  7. java编程思想-泛型思维导图

  8. Object-Widgets-Quick 构造树

    Object Tree 当以某个QObject为父类创建一个QObject时, 它会被添加到该父类的children列表中. 析构时, QObjet 会首先检查自己的children, 依次析构, 然 ...

  9. 消息队列&lowbar;MSMQ(2)简单应用

    上一篇讲了MSMQ的简单知识,那这次我们讲下简单代码的知识 附上源码: https://gitee.com/592576605/MSMQ_HANS 下面是简单的类库说明,具体咋用就看源码吧 类(Cla ...

  10. VS2017插件开发-项目右键菜单

    1.创建自定义命令 2.更改.vsct中Group节点的id <Group guid="guidPublishOwinPackageCmdSet1" id="MyM ...