概述

本章继续学习ViewResolver另一个实现类ContentNegotiatingViewResolver解析器,该类的主要作用是根据同一请求的某些策略,选择对应的View进行渲染。可以把ContentNegotiatingViewResolver理解为适配器,对不同类型View进行适配。值得注意的是处理的handler为同一个。

ContentNegotiatingViewResolver本身不解析视图,它将委托其他视图解析器进行视图解析。

请求的策略包括:请求后缀、请求头的Accept、使用参数等等。

本系列文章是基于Spring5.0.5RELEASE。

流程概述

使用此视图解析器时,调用resolverViewName(viewName,locale)方法,首先调用本类的getMediaType(reuqest)获取请求的媒体类型mediaType(根据策略),然后调用getCandidateViews()方法解析归并到View集合,最后调用getBestView()方法,根据contentType选择出合适的视图并返回。

源码分析

  • ContentNegotiatingViewResolver

该类主要完成

public class ContentNegotiatingViewResolver extends WebApplicationObjectSupportimplements ViewResolver, Ordered, InitializingBean {// 判断请求mediaType,内部包含使用的策略集合
@Nullable
private ContentNegotiationManager contentNegotiationManager;
// 用于创建ContentNegotiationManager实例
private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean();
// 控制为查找到时的处理
private boolean useNotAcceptableStatusCode = false;
// 存储View实例,可在此集合查询符合条件的View实例进行视图渲染
@Nullable
private List<View> defaultViews;
// 视图解析器集合,用于解析视图
@Nullable
private List<ViewResolver> viewResolvers;
// 排序属性
private int order = Ordered.HIGHEST_PRECEDENCE;... ...
/***启动时从上下文中加载ViewResolver*/
@Override
protected void initServletContext(ServletContext servletContext) {// 从上下文中获取所有视图解析器Collection<ViewResolver> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();if (this.viewResolvers == null) {// 将上下文配置的视图解析器添加到属性viewResolvers中,以供后续使用this.viewResolvers = new ArrayList<>(matchingBeans.size());for (ViewResolver viewResolver : matchingBeans) {if (this != viewResolver) {this.viewResolvers.add(viewResolver);}}}else {// 初始化viewResolvers属性中配置的视图解析器for (int i = 0; i < this.viewResolvers.size(); i++) {ViewResolver vr = this.viewResolvers.get(i);if (matchingBeans.contains(vr)) {continue;}String name = vr.getClass().getName() + i;obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);}}if (this.viewResolvers.isEmpty()) {logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +"'viewResolvers' property on the ContentNegotiatingViewResolver");}// 排序视图解析器AnnotationAwareOrderComparator.sort(this.viewResolvers);this.cnmFactoryBean.setServletContext(servletContext);
}/**启动时调用,如果没有配置ContentNegotiationManager,启动时进行创建初始化该属性*/
@Override
public void afterPropertiesSet() {if (this.contentNegotiationManager == null) {this.contentNegotiationManager = this.cnmFactoryBean.build();}
}/**请求到来时匹配核实的View并返回*/
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {RequestAttributes attrs = RequestContextHolder.getRequestAttributes();Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");// 获取请求的mediaTypeList<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());if (requestedMediaTypes != null) {// 解析出所有视图View和配置的默认View合并,供后面从中匹配选择List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);// 根据contentType匹配选择出合适的ViewView bestView = getBestView(candidateViews, requestedMediaTypes, attrs);// 返回if (bestView != null) {return bestView;}}// 未匹配到合适的View,并且把参数useNotAcceptableStatusCode设置为true时,返回406if (this.useNotAcceptableStatusCode) {if (logger.isDebugEnabled()) {logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");}return NOT_ACCEPTABLE_VIEW;}else {logger.debug("No acceptable view found; returning null");return null;}... ...
}

以上是ContentNegotiatingViewResolver类的主要代码,具体调用的方法再此不再展开,有兴趣的童鞋可以自行查看,下面我们来使用这个解析器做个例子,通过例子再加深下理解。

实战

  • 需求目标

实现后缀名或参数控制,显示不同的视图。如:

http://localhost:8088/user jsp视图显示

http://localhost:8088/user.json(http://localhost:8088/user?format=json) json视图显示

http://localhost:8088/user.xml(http://localhost:8088/user?format=xml) xml视图显示

  • 项目结构

新建maven web项目,最终目录结构如下:

  • pom文件

通过maven引入jar依赖,代码如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.github.dalianghe</groupId><artifactId>spring-mvc-viewresolver-03</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>spring-mvc-viewresolver-03 Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><!-- 依赖spring mvc --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.0.5.RELEASE</version></dependency><!-- servlet依赖 --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- jsp依赖 --><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><!-- json依赖 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.9.5</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.5</version></dependency><!--xml依赖--><dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.5</version></dependency></dependencies><build><finalName>spring-mvc-viewresolver-03</finalName><pluginManagement><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><path>/</path><port>8088</port></configuration></plugin></plugins></pluginManagement></build>
</project>
  • spring配置文件

配置视图解析器等,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"default-autowire="byName"><!-- 扫描指定路径 --><context:component-scan base-package="com.github.dalianghe.controller"/><!-- 配置ContentNegotiationManagerFactoryBean构造ContentNegotiationManager实例 --><bean id="cnManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"><!-- 忽略accept header,即禁用HeaderContentNegotiationStrategy策略 --><property name="ignoreAcceptHeader" value="true"/><!-- 是否启用扩展名支持,即支持PathExtensionContentNegotiationStrategy策略 --><property name="favorPathExtension" value="true"></property><!-- 是否启用参数支持,即支持ParameterContentNegotiationStrategy策略 --><property name="favorParameter" value="true"></property><!--<property name="defaultContentType" value="text/html"/>--><property name="mediaTypes"><map><entry key="xml" value="application/xml"/><!--<entry key="json" value="text/plain"/>--><entry key="json" value="application/json"/><!--<entry key="xls" value="application/vnd.ms-excel"/>--></map></property></bean><!-- 配置视图解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><property name="contentNegotiationManager" ref="cnManager"/><property name="viewResolvers"><list><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean></list></property><!-- 默认支持的View --><property name="defaultViews"><list><bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"><!--<property name="prettyPrint" value="true"/>--><property name="contentType" value="application/json"/></bean><bean class="org.springframework.web.servlet.view.xml.MappingJackson2XmlView"><property name="contentType" value="application/xml"/></bean></list></property><!--<property name="useNotAcceptableStatusCode" value="true"/>--></bean></beans>
  • 部署描述文件

配置DispatcherServlet,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><display-name>Archetype Created Web Application</display-name><servlet><!-- Servlet名称,可任意定义,但必须与servlet-mapping中对应 --><servlet-name>dispatcher</servlet-name><!-- 指定Spring MVC核心控制类,即J2EE规范中的前端控制器(Front Controller) --><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 指定Spring MVC配置文件,默认在WEB-INF目录下,切名字为[servlet-name]-servlet.xml,此文件中配置web相关内容,比如:指定扫描Controller路径、配置逻辑视图前缀后缀、上传文件等等 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-servlet.xml</param-value></init-param><!-- 此配置的值为正整数时,表示容器启动时初始化,即调用Servlet的init方法 --><load-on-startup>1</load-on-startup><async-supported>true</async-supported></servlet><!-- 定义servlet映射 --><servlet-mapping><!-- 与servlet中servlet-name对应 --><servlet-name>dispatcher</servlet-name><!-- 映射所有的url --><url-pattern>/</url-pattern></servlet-mapping></web-app>
  • User实体

简单的实体类,代码如下:

public class User{private String username;private String password;public User(String username, String password) {this.username = username;this.password = password;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
  • handler处理器

编写Controller,代码如下:

import com.github.dalianghe.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;@Controller
public class DemoController {@GetMapping("/user")public String demo(ModelMap model){User user = new User("hedaliang", "123456");model.addAttribute(user);return "user";}
}
  • jsp页面

jsp视图(user.jsp),代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>My Frist JSP</title>
</head>
<body>
<h1>username:${user.username}</h1><br>
<h1>password:${user.password}</h1>
</body>
</html>

以上代码编写结束,下面来进行测试。

测试

启动应用,访问地址:http://localhost:8080/user,此时应使用jsp进行渲染,结果如下:

访问http://locahost:8088/user.json或http://localhost:8088/user?format=json,结果如下:

访问http://localhost:8088/user.xml或http://localhost:8088/user?format=xml,结果如下:

OK!跟预期一致,测试通过,至此我们就实现了需求功能。

总结

本章介绍了ContentNegotiatingViewResolver类,并通过开发小demo验证了此类的功能,里面细节很多,有兴趣的朋友可以再深入了解,希望本文能给大家一写启发。

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

SpringMVC之源码分析--ViewResolver(四)相关推荐

  1. spring源码分析第四天------springmvc核心原理及源码分析

    spring源码分析第四天------springmvc核心原理及源码分析 1.基础知识普及 2. SpringMVC请求流程 3.SpringMVC代码流程 4.springMVC源码分析 4.1 ...

  2. Flume 1.7 源码分析(四)从Source写数据到Channel

    Flume 1.7 源码分析(一)源码编译 Flume 1.7 源码分析(二)整体架构 Flume 1.7 源码分析(三)程序入口 Flume 1.7 源码分析(四)从Source写数据到Channe ...

  3. Anbox源码分析(四)——Anbox渲染原理(源码分析)

    Anbox源码分析(四) 上篇文章我们从源码分析了一下Anbox是怎样一步步的准备了OpenGL ES的渲染环境的,这篇文章,我们继续分析Android的渲染指令是如何到达宿主机进行渲染的. 宿主机端 ...

  4. shardingsphere源码分析(四)-- 改写引擎

    shardingsphere源码分析(四)-- 改写引擎 shardingsphere源码分析(四)-- 改写引擎 官方介绍 debug 总结 shardingsphere源码分析(四)-- 改写引擎 ...

  5. kube-scheduler源码分析(四)之 findNodesThatFit

    本文个人博客地址:https://www.huweihuang.com/kubernetes-notes/code-analysis/kube-scheduler/findNodesThatFit.h ...

  6. live555源码分析(四)RTSPServer分析

    live555源码分析系列 live555源码分析(一)live555初体验 live555源码分析(二)基本组件上 live555源码分析(三)基本组件下 live555源码分析(四)RTSPSer ...

  7. webpack 源码分析(四)——complier模块

    webpack 源码分析(四)--complier模块 上一篇我们看到,webpack-cli 通过 yargs 对命令行传入的参数和配置文件里的配置项做了转换包装,然后传递给 webpack 的 c ...

  8. SpringMVC之源码分析--LocaleResolver和ThemeResolver应用

    概述 以上分析了Spring MVC的LocaleResolver和ThemeResolver两个策略解析器,在实际项目中很少使用,尤其是ThemeResolver,花精力去分析他们,主要是为了系统的 ...

  9. Docker源码分析(四):Docker Daemon之NewDaemon实现

    http://www.infoq.com/cn/articles/docker-source-code-analysis-part4 1. 前言 Docker的生态系统日趋完善,开发者群体也在日趋庞大 ...

最新文章

  1. 用nginx搭建基于rtmp或者http的flv、mp4流媒体服务器
  2. 【译】为什么这样宏定义#define INT_MIN (-2147483647 - 1)?
  3. 从C语言的角度重构数据结构系列(五)-C语言的程序结构和基本语法
  4. mysql批量加逗号_批量插入数据到数据库的方式
  5. 某系统有6台输出设备 有多个进程均需要使用2台_从零开始学K8s: 2.开发与部署方式的演变...
  6. Windows——双系统环境下没有启动Windows 启动管理器(自动跳过操作系统选择界面)解决方案
  7. 对大学 IT 专业教育的反馈
  8. matlab根据转移函数绘制放大器幅频响应和相频响应
  9. VS2010下的多线程窗口
  10. java jar包示例_Java包getImplementationVersion()方法和示例
  11. Bootstrap媒体对象
  12. 服务器中安装多版本的cuda
  13. Python面试题_中级版
  14. 数据结构笔记--线性表定义与实现(Swift)
  15. matlab costas环,MATLAB写COSTAS环的问题。
  16. Apache新成员:LinkedIn分布式数据集成框架Gobblin
  17. PHP校园食堂系统描述
  18. 5G垂直领域:华为智慧园区
  19. QQ小游戏 微信小游戏 即时通信 IM 删除会话 deleteConversation sdk
  20. 数字音乐收费,一场困兽犹斗

热门文章

  1. IE10访问apache 2.4会奇慢的解决办法
  2. SDUT 2384 El Dorado
  3. 如何增加MOSS 2007中list template和site template的最大值
  4. App Store生存法则:iOS开发者经验分享
  5. mysql中使用join exists in时该注意的问题
  6. Python链接Mssql之Python库pymssql
  7. [其他]JAVA与C#的Socket通信
  8. 141. Linked List Cycle
  9. Mac环境下用Java(Sikuli+Robot)实现页游自动化
  10. .net中的对象序列化(1): 序列化是什么, 以及一个简单的例子