SpringMVC之源码分析--ViewResolver(四)
概述
本章继续学习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(四)相关推荐
- spring源码分析第四天------springmvc核心原理及源码分析
spring源码分析第四天------springmvc核心原理及源码分析 1.基础知识普及 2. SpringMVC请求流程 3.SpringMVC代码流程 4.springMVC源码分析 4.1 ...
- Flume 1.7 源码分析(四)从Source写数据到Channel
Flume 1.7 源码分析(一)源码编译 Flume 1.7 源码分析(二)整体架构 Flume 1.7 源码分析(三)程序入口 Flume 1.7 源码分析(四)从Source写数据到Channe ...
- Anbox源码分析(四)——Anbox渲染原理(源码分析)
Anbox源码分析(四) 上篇文章我们从源码分析了一下Anbox是怎样一步步的准备了OpenGL ES的渲染环境的,这篇文章,我们继续分析Android的渲染指令是如何到达宿主机进行渲染的. 宿主机端 ...
- shardingsphere源码分析(四)-- 改写引擎
shardingsphere源码分析(四)-- 改写引擎 shardingsphere源码分析(四)-- 改写引擎 官方介绍 debug 总结 shardingsphere源码分析(四)-- 改写引擎 ...
- kube-scheduler源码分析(四)之 findNodesThatFit
本文个人博客地址:https://www.huweihuang.com/kubernetes-notes/code-analysis/kube-scheduler/findNodesThatFit.h ...
- live555源码分析(四)RTSPServer分析
live555源码分析系列 live555源码分析(一)live555初体验 live555源码分析(二)基本组件上 live555源码分析(三)基本组件下 live555源码分析(四)RTSPSer ...
- webpack 源码分析(四)——complier模块
webpack 源码分析(四)--complier模块 上一篇我们看到,webpack-cli 通过 yargs 对命令行传入的参数和配置文件里的配置项做了转换包装,然后传递给 webpack 的 c ...
- SpringMVC之源码分析--LocaleResolver和ThemeResolver应用
概述 以上分析了Spring MVC的LocaleResolver和ThemeResolver两个策略解析器,在实际项目中很少使用,尤其是ThemeResolver,花精力去分析他们,主要是为了系统的 ...
- Docker源码分析(四):Docker Daemon之NewDaemon实现
http://www.infoq.com/cn/articles/docker-source-code-analysis-part4 1. 前言 Docker的生态系统日趋完善,开发者群体也在日趋庞大 ...
最新文章
- 用nginx搭建基于rtmp或者http的flv、mp4流媒体服务器
- 【译】为什么这样宏定义#define INT_MIN (-2147483647 - 1)?
- 从C语言的角度重构数据结构系列(五)-C语言的程序结构和基本语法
- mysql批量加逗号_批量插入数据到数据库的方式
- 某系统有6台输出设备 有多个进程均需要使用2台_从零开始学K8s: 2.开发与部署方式的演变...
- Windows——双系统环境下没有启动Windows 启动管理器(自动跳过操作系统选择界面)解决方案
- 对大学 IT 专业教育的反馈
- matlab根据转移函数绘制放大器幅频响应和相频响应
- VS2010下的多线程窗口
- java jar包示例_Java包getImplementationVersion()方法和示例
- Bootstrap媒体对象
- 服务器中安装多版本的cuda
- Python面试题_中级版
- 数据结构笔记--线性表定义与实现(Swift)
- matlab costas环,MATLAB写COSTAS环的问题。
- Apache新成员:LinkedIn分布式数据集成框架Gobblin
- PHP校园食堂系统描述
- 5G垂直领域:华为智慧园区
- QQ小游戏 微信小游戏 即时通信 IM 删除会话 deleteConversation sdk
- 数字音乐收费,一场困兽犹斗
热门文章
- IE10访问apache 2.4会奇慢的解决办法
- SDUT 2384 El Dorado
- 如何增加MOSS 2007中list template和site template的最大值
- App Store生存法则:iOS开发者经验分享
- mysql中使用join exists in时该注意的问题
- Python链接Mssql之Python库pymssql
- [其他]JAVA与C#的Socket通信
- 141. Linked List Cycle
- Mac环境下用Java(Sikuli+Robot)实现页游自动化
- .net中的对象序列化(1): 序列化是什么, 以及一个简单的例子