内容协商在视图View上的应用【享学Spring MVC】

人生很有意思:首先就得活得长。活得长才能够见自己,再长就可以见众生

前言

在经过 前两篇 文章了解了Spring MVC的内容协商机制之后,相信你已经能够熟练的运用Spring MVC提供的这项能力,配合RESTful发挥它的功效了。这其实也就达到了我们目的的80%,也达到了我书写这块知识点的目的。

为何说是80%呢?因为我认为在前后端完全分离的今天,绝大部分使用场景都是这种情况,完成了覆盖。
为何还有20%呢?因为内容协商不仅仅可以使用在HttpMessage上,还可以使用在View视图上,这也就是本文想重点补充的内容。

内容协商在HttpMessage上的应用

前两篇文章的示例都是基于此。在讲解原理的时候提到:处理的入口在AbstractMessageConverterMethodProcessor.writeWithMessageConverters()方法上,看此抽象类的子类也能看出端倪:

在这里插入图片描述


从子类实现中你也能够明白:它和HttpMessage是强相关的,都是经过了HttpMessageConverter处理的消息来做内容协商。

这两个实现类处理的也就是我们当下最为常用的注解:@ResponseBody。或者返回值直接是HttpEntity/ResponseEntity类型(也就是不能是RequestEntity就成)

毫无疑问,基于@ResponseBody的Rest接口方式在前后端完全分离的今天已然是主流方式,因此我说前两篇文章覆盖了80%的场景应该不为过吧~

我搜索到ContentNegotiationManager.resolveMediaTypes()方法在ContentNegotiatingViewResolver里也使用到了,因此我自然而然的联想到了内容协商也能结合视图解析器一起使用~

内容协商在视图View上的应用

由于前面我给的示例都是基于Http消息的,没有视图可言。本文此处需要讲解的是内容协商在视图解析方面的应用:同一个URL,以不同的视图作为展示方式

我们已经知道了:RequestMappingInfoHandlerMapping(@RequestMapping)它在对带有后缀的http请求进行匹配的时候,如果找不到精确的pattern, 那么就会pattern+.*后再匹配 url,它会处理多个不同形式是 url,但是返回的是同一个View。本文就教你用一个@RequestMapping也能返回多个View~

注意:我这里指的是返回的是View视图,对于消息体的这种返回方式,不是本处讨论的范畴,它属于case 1。

视图解析器ViewResolver

关于视图的内容,可参见这里:View
关于视图解析器的内容,可参见这里:ViewResolver

本文简单的再“复习”一下Spring MVC对视图解析器的使用流程:

使用处:DispatcherServlet.resolveViewName()
得到逻辑视图后,通过已经注册好的视图解析器ViewResolver把逻辑视图解析为真正的视图View

DispatcherServlet: @Nullable protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) { // 按照顺序:一个一个执行。第一个最先解析到不返回null的 就是最终返回的view视图 for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null; }

加载处:DispatcherServlet.initViewResolvers()
这个在讲解Spring MVC九大组件加载时详细说过

DispatcherServlet: private void initViewResolvers(ApplicationContext context) { // 1、若detectAllViewResolvers=true,去容器中找到所有的ViewResolver Bean们。排序后返回 // 2、若不是探测全部。就只找BeanName=viewResolver它的这一个Bean // 2、若一个都没有找到,就走默认策略:从DispatcherServlet.properties里配置的读取默认的配置 }

这个查找策略,对我们合理注册、管理视图解析器都是很有用的,可以稍加留意

声明处:WebMvcConfigurationSupport.mvcViewResolver()

WebMvcConfigurationSupport: // @since 4.1 向容器注册一个ViewResolver Bean // 使用的是容器管理方式:ViewResolverComposite @Bean public ViewResolver mvcViewResolver() { // mvcContentNegotiationManager:内容协商管理器(本文重点之一) ViewResolverRegistry registry = new ViewResolverRegistry(mvcContentNegotiationManager(), this.applicationContext); // protected方法,回调给我们调用者,允许自定义ViewResolverRegistry configureViewResolvers(registry); // 它的意思是:如果你没有自定义(或者自定义了但一个解析器都木有) // 那就主动去容器里找。如果仅仅仅仅只知道一个:那它就是InternalResourceViewResolver(注意此处是new的) // 注意此处的处理方式哦~~~~ if (registry.getViewResolvers().isEmpty() && this.applicationContext != null) { String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.applicationContext, ViewResolver.class, true, false); if (names.length == 1) { registry.getViewResolvers().add(new InternalResourceViewResolver()); } } // 最终使用ViewResolverComposite把这些(多个)装起来,便于管理~ ViewResolverComposite composite = new ViewResolverComposite(); composite.setOrder(registry.getOrder()); composite.setViewResolvers(registry.getViewResolvers()); if (this.applicationContext != null) { composite.setApplicationContext(this.applicationContext); } if (this.servletContext != null) { composite.setServletContext(this.servletContext); } return composite; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsspx.html