java dispatcher详解_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.

*

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的继承关系:

org.springframework.web.servlet.view.AbstractView

org.springframework.web.servlet.view.AbstractUrlBasedView

org.springframework.web.servlet.view.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 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 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 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.

小结:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值