单位网站建设目的,wordpress 数据库合并,重庆涪陵网站设计公司推荐,wordpress菜单分类目录一、本来想说的是返回值处理问题#xff0c;但在 SpringMVC 中#xff0c;返回值处理问题的核心就是视图渲染。所以这里标题叫视图渲染问题。 本来想在上一篇文章中对视图解析进行说明的#xff0c;但是通过源码发现#xff0c;它应该算到视图渲染中#xff0c;所以在这篇…一、本来想说的是返回值处理问题但在 SpringMVC 中返回值处理问题的核心就是视图渲染。所以这里标题叫视图渲染问题。 本来想在上一篇文章中对视图解析进行说明的但是通过源码发现它应该算到视图渲染中所以在这篇文章中进行说明。 org.springframework.web.servlet.DispatcherServlet#doDispatch方法中 mv ha.handle(processedRequest, response, mappedHandler.getHandler());//945行返回了 ModelAndView 对象 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);// 959行进行的就是返回值处理问题 org.springframework.web.servlet.DispatcherServlet#processDispatchResult方法中 render(mv, request, response); //1012进行视图的渲染包含视图解析 org.springframework.web.servlet.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.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;}} 可以看到有两个 if 第一个 if 解决的是视图解析问题第二个 if 解决的是视图渲染问题。还有官方是这样描述这个方法的Render the given ModelAndView. 二、视图解析通过视图解析器进行视图的解析 1.解析一个视图名到一个视图对象具体解析的过程是在容器中查找所有配置好的视图解析器List类型然后进行遍历 只要有一个视图解析器能解析出视图就返回 View 对象若遍历完成后都不能解析出视图那么返回 null。 具体来看 org.springframework.web.servlet.DispatcherServlet#resolveViewName protected View resolveViewName(String viewName, MapString, Object model, Locale locale,HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) {View view viewResolver.resolveViewName(viewName, locale);if (view ! null) {return view;}}return null;
} 2. ViewResolver 1官方描述 * Interface to be implemented by objects that can resolve views by name. * * pView state doesnt change during the running of the application, * so implementations are free to cache views. * * pImplementations are encouraged to support internationalization, * i.e. localized view resolution. 说明 ViewResolver 接口由能解析视图名称的实现类来实现。 在程序运行期间视图的状态不能更改所以实现能被随意缓存。鼓励实现支持国际化。 2ViewResolver 的整个体系 可以看出 SpringMVC 提供了很多类型视图解析器。 3在 SpringMVC 的第一篇文章中配置过一个视图解析器。 bean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/views//property namesuffix value.jsp/
/bean 发送一个请求之后发现要遍历的 ViewResolvers 只有一个就是上面的这个 ViewResolver。没有其他默认的视图解析器。所以说在SpringMVC 配置文件中必须配置至少一个 视图解析器。 那么这里会有一个问题如果配置多个视图解析器他们的遍历顺序是怎么样的呢 ViewResolver 的所有实现类中都存在一个 order 属性。 看这个属性的 setOrder() 注释Set the order in which this {link org.springframework.web.servlet.ViewResolver} is evaluated。设置谁先被评估。 还有一点小不同 除 ContentNegotiatingViewResolver 之外其他所有的 ViewResolver 的默认值都是Integer.MAX_VALUE2^31 -1即2147483647 而 ContentNegotiatingViewResolver 的默认值为 Ordered.HIGHEST_PRECEDENCE-2147483648。 那他们的遍历的顺序与 order 是什么关系呢如果不设置 order 的话遍历顺序又是怎么样的 1设置 order 属性后遍历顺序是怎么样的 bean classorg.springframework.web.servlet.view.BeanNameViewResolverproperty nameorder value99/
/beanbean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/views//property namesuffix value.jsp/property nameorder value-99/
/bean 对 BeanNameViewResolver 的 order 属性指定为 99对 InternalResourceViewResolver 指定为-99。这里故意将 BeanNameViewResolver 放到了 InternalResourceViewResolver 前面。 遍历顺序 发现 InternalResourceViewResolver 会先被遍历。 结论 在指定 order 属性的情况下order 值越小的越先会遍历。 2不设置 order 属性遍历顺序是怎样的 在测试这个的时候发现一个这样的现象我将 BeanNameViewResolver 和 InternalResourceViewResolver 的 order 属性都去掉我这里用的是 Jrebel 的热部署。发现再次请求的时候 这两个视图的 order 属性值还和之前的一样 bean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/views//property namesuffix value.jsp/
/beanbean classorg.springframework.web.servlet.view.BeanNameViewResolver
/bean 为什么呢想起了官方的描述在程序运行期间视图的状态不能更改所以实现能被随意缓存这里被缓存了。重启后来看真正的测试。 第一种情况 bean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/views//property namesuffix value.jsp/
/beanbean classorg.springframework.web.servlet.view.BeanNameViewResolver
/bean 第二种情况将 bean 在 SpringMVC Config 文件中的顺序进行替换需要注意的是重启服务器否则它们的顺序还是会被缓存下来。重启后来看 bean classorg.springframework.web.servlet.view.BeanNameViewResolver
/beanbean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/views//property namesuffix value.jsp/
/bean 结论已经很明显了 在同等优先级的情况下遍历的顺序是由 ViewResolver 在 SpringMVC Config 文件中配置的顺序决定的谁在前谁先遍历。 这里不对具体的每个视图解析器进行说明路已经指明了。 3.ViewResolver 具体是怎么将 view name 解析为一个视图的 先看 ViewResolver 中的 View resolveViewName(String viewName, Locale locale) 说明 解析视图通过其名称。注意允许 ViewResolver 链。 如果一个给定名称的view 没有在一个 ViewResolver 中定义那么它应该返回 null。 然而它也不是必须的有一些 ViewResolver 当尝试通过视图名称构建 View 对象失败后不返回 null。而替代它的是抛出一个异常。 以 org.springframework.web.servlet.view.AbstractCachingViewResolver 进行分析。 Override
public View resolveViewName(String viewName, Locale locale) throws Exception {if (!isCache()) {return createView(viewName, locale);}else {Object cacheKey getCacheKey(viewName, locale);View view this.viewAccessCache.get(cacheKey);if (view null) {synchronized (this.viewCreationCache) {view this.viewCreationCache.get(cacheKey);if (view null) {// Ask the subclass to create the View object.view createView(viewName, locale);if (view null this.cacheUnresolved) {view UNRESOLVED_VIEW;}if (view ! null) {this.viewAccessCache.put(cacheKey, view);this.viewCreationCache.put(cacheKey, view);if (logger.isTraceEnabled()) {logger.trace(Cached view [ cacheKey ]);}}}}}return (view ! UNRESOLVED_VIEW ? view : null);}
} 判断该视图是否被缓存如果没有被缓存则创建视图如果被缓存则从缓存中获取。 创建视图以 InternalResourceViewResolver 和 BeanNameViewResolver 为例 1InternalResourceViewResolver org.springframework.web.servlet.view.UrlBasedViewResolver#createView protected View createView(String viewName, Locale locale) throws Exception {// If this resolver is not supposed to handle the given view,// return null to pass on to the next resolver in the chain.if (!canHandle(viewName, locale)) {return null;}// Check for special redirect: prefix.if (viewName.startsWith(REDIRECT_URL_PREFIX)) {String redirectUrl viewName.substring(REDIRECT_URL_PREFIX.length());RedirectView view new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());return applyLifecycleMethods(viewName, view);}// Check for special forward: prefix.if (viewName.startsWith(FORWARD_URL_PREFIX)) {String forwardUrl viewName.substring(FORWARD_URL_PREFIX.length());return new InternalResourceView(forwardUrl);}// Else fall back to superclass implementation: calling loadView.return super.createView(viewName, locale);
} 在创建视图前会检查返回值是否是以redirect: 或 forward: 开头的。 如果是重定向则创建一个重定向视图返回创建的视图。如果是转发则返回通过 转发 url 创建的 InternalResourceView 视图。 org.springframework.web.servlet.view.UrlBasedViewResolver#loadView AbstractUrlBasedView view buildView(viewName);
View result applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null); 调用具体的 InternalResourceViewResolver 然后又调用 父类的 buildView() 方法 org.springframework.web.servlet.view.UrlBasedViewResolver#buildView protected AbstractUrlBasedView buildView(String viewName) throws Exception {AbstractUrlBasedView view (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());view.setUrl(getPrefix() viewName getSuffix());String contentType getContentType();if (contentType ! null) {view.setContentType(contentType);}view.setRequestContextAttribute(getRequestContextAttribute());view.setAttributesMap(getAttributesMap());if (this.exposePathVariables ! null) {view.setExposePathVariables(exposePathVariables);}return view;
} 可以看出是通过 BeanUtils.instantiateClass(getViewClass()) 来创建 View 对象的。这个例子与其说是 InternalResourceViewResolver 倒不如说是 UrlBasedViewResolver 类型的例子。 从这里也可以看出该类型最终要到的目标URL为getPrefix() viewName getSuffix() 2BeanNameViewResolver public View resolveViewName(String viewName, Locale locale) throws BeansException {ApplicationContext context getApplicationContext();if (!context.containsBean(viewName)) {// Allow for ViewResolver chaining.return null;}return context.getBean(viewName, View.class);
} org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.String, java.lang.ClassT) public T T getBean(String name, ClassT requiredType) throws BeansException {this.assertBeanFactoryActive();return this.getBeanFactory().getBean(name, requiredType);
} 可以看出是通过 BeanFactory.getBean(String name, ClassT requiredType) 来获取的。 三、视图渲染 1.View 官方文档 * MVC View for a web interaction. Implementations are responsible for rendering* content, and exposing the model. A single view exposes multiple model attributes.** pThis class and the MVC approach associated with it is discussed in Chapter 12 of* a hrefhttp://www.amazon.com/exec/obidos/tg/detail/-/0764543857/Expert One-On-One J2EE Design and Development/a* by Rod Johnson (Wrox, 2002).** pView 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.** pViews 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. 说明 SpringMVC 对一个 web 来说是相互作用的不太明白。View 的实现类是负责呈现内容的并且 exposes暴露、揭露、揭发的意思这里就按暴露解释吧想不出合适的词语 模型的。 一个单一的视图可以包含多个模型。 View 的实现可能有很大的不同。一个明显的实现是基于 JSP 的。其他的实现可能是基于 XSLT 的或者是一个 HTML 生成库。 设计这个接口是为了避免约束可能实现的范围这里是不是说我们可以通过实现该接口来自定义扩展自定义视图。 所有的视图都应该是一个 Bean 类。他们可能被 ViewResolver 当做一个 bean 进行实例化。 由于这个接口是无状态的View 的所有实现类应该是线程安全的。 2.View 的整个体系 3.具体渲染的一个过程 org.springframework.web.servlet.view.AbstractView#render 说明一下这个方法 为指定的模型指定视图如果有必要的话合并它静态的属性和RequestContext中的属性renderMergedOutputModel() 执行实际的渲染。 public void render(MapString, ? 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);}MapString, Object mergedModel createMergedOutputModel(model, request, response);prepareResponse(request, response);renderMergedOutputModel(mergedModel, request, response);
} 这里只看 org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel 这个方法 Override
protected void renderMergedOutputModel(MapString, 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);}
} 可以看到前面的几个步骤都是为 RequestDispatch 做准备装填数据。最后到目标页面是通过转发。 四、总结 介绍了 SpringMVC 视图解析和视图渲染问题是如何解决的。SpringMVC 为逻辑视图提供了多种视图解析策略可以在配置文件中配置多个视图的解析策略。并制定其先后顺序。 这里所说的视图解析策略就是指视图解析器。视图解析器会将逻辑视图名解析为一个具体的视图对象。再说视图渲染的过程视图对模型进行了渲染最终将模型的数据以某种形式呈现给用户。 到此为止在我看来SpringMVC 整个流程已经跑完前面的几篇文章从一个小栗子开始然后分别介绍了请求映射问题参数问题返回值问题到这篇文章返回值处理问题。 我认为这几个问题是整个 SpringMVC 的核心内容。其他的问题都是建立在该问题的基础上或者是对这几个问题的一种延伸。 还有想说的是在边测试边看源码边写文章的时候给我这么一个感觉不论是何种开源框架它所有的应用都是从源码中来的不论哪个人或者那本书把这个知识讲的多好 但是在我看来想要学好某个框架都要到它源码中去找到它的根这样才会有理有据不会忘的那么快纵然忘了下次还是可以找出来为什么。 纵然在看源码的过程中可能会遇到很多困难比如英文单词不认识整个句子读不通但是如果你能结合自己的理解 然后理解文档中的话我相信对你帮助是很大的其实这就是一种自学能力摸的着看得见。 算是给各位看客老爷的一种建议吧愿各位在学习的道路上不要停滞不前。 转载于:https://www.cnblogs.com/solverpeng/p/5743609.html