Spring MVC学习(4)—ViewSolvsolver视图解析器的详细介绍与使用案例

发布时间 2024-01-08 18:19:24作者: l_v_y_forever

基于最新Spring 5.x,详细介绍了Spring MVC的ViewSolvsolver视图解析器组件,并提供了详细的使用案例。

转载自:https://blog.csdn.net/weixin_43767015/article/details/116758079

此前我们学习了Spring MVC中的核心组件以及请求的执行流程,现在让我们专门来学习ViewSolvsolver 视图解析器组件的功能和使用。

Spring MVC学习 系列文章

Spring MVC学习(1)—MVC的介绍以及Spring MVC的入门案例

Spring MVC学习(2)—Spring MVC中容器的层次结构以及父子容器的概念

Spring MVC学习(3)—Spring MVC中的核心组件以及请求的执行流程

Spring MVC学习(4)—ViewSolvsolver视图解析器的详细介绍与使用案例

Spring MVC学习(5)—基于注解的Controller控制器的配置全解【一万字】

Spring MVC学习(6)—Spring数据类型转换机制全解【一万字】

Spring MVC学习(7)—Validation基于注解的声明式数据校验机制全解【一万字】

Spring MVC学习(8)—HandlerInterceptor处理器拦截器机制全解

Spring MVC学习(9)—项目统一异常处理机制详解与使用案例

Spring MVC学习(10)—文件上传配置、DispatcherServlet的路径配置、请求和响应内容编码

Spring MVC学习(11)—跨域的介绍以及使用CORS解决跨域问题

 

 

1 ViewSolvsolver 的概述

Spring MVC 定义了 ViewSolvsolver 和 View 接口,这些接口允许我们在Web应用中通过模型数据对视图模版进行渲染并返回给浏览器,而无需依赖特定的视图技术。

ViewResolver 提供逻辑视图名称到实际物理视图的映射,即将逻辑视图名解析为View视图对象,其中包含了真实物理视图的信息(比如url路径、locale国际化信息)。View 则用于在将模型数据移交给特定视图技术之前进行数据准备,随后调用对应的视图技术进行视图渲染。

ViewResolver的不同实现具有不同的逻辑视图名到物理视图的映射算法,常见实现的uml类图结构如下:

在这里插入图片描述

1.1 AbstractCachingViewResolver

实现了ViewResolver接口的便捷抽象基类。继承了AbstractCachingViewResolver的子类将默认缓存它们已经解析了的View视图实例,这意味着无论初始View视图的检索成本有多高,视图解析都不会是性能问题,即缓存可以提高某些视图技术的性能。

我们可以通过将cache属性设置为false来关闭缓存。此外,如果必须在运行时刷新某个视图(例如,当修改FreeMarker模板时),则可以使用removeFromCache(String viewName,Locale loc)方法。
默认最大缓存1024个,缓存的View的key为viewName和locale的组合:

在这里插入图片描述

2 XmlViewResolver

ViewResolver的一种实现,继承了AbstractCachingViewResolver,具有缓存View视图的能力。

XmlViewResolver支持接受XML配置文件,配置文件中存放的是自己配置的View bean,每一个View bean都有自己的id/name,XmlViewResolver将根据Controller处理器方法返回的逻辑视图名称到指定的XML配置文件中寻找对应名称的 View bean并返回,用于处理视图,这就是它的工作(将逻辑视图名解析为真正的View视图对象)原理!

默认配置文件是/WEB-INF/views.xml,如果不使用默认配置,那么可以在XmlViewResolver的location属性中指定它的位置。

XmlViewResolver指定的cachekey就是viewName:

在这里插入图片描述

2.1 使用案例

如下案例,一个Controller:

/**
 * @author lx
 */
@Controller
public class XmlViewResolverController {

    @RequestMapping("/xmlViewResolver")
    public ModelAndView handle() {
        ModelAndView modelAndView = new ModelAndView();
        //添加模型数据
        modelAndView.addObject("msg", "xmlViewResolver测试");
        //添加逻辑视图名
        modelAndView.setViewName("xvr");
        return modelAndView;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

spring-mvc-config.xml配置文件,我们手动注册了一个XmlViewResolver,并指定配置文件位置为类路径下面(resources下面)的views.xml文件!

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--扫描包-->
    <context:component-scan base-package="com.spring.mvc"/>

    <!--手动配置一个XmlViewResolver-->
    <bean class="org.springframework.web.servlet.view.XmlViewResolver">
        <!--指定配置文件位置-->
        <property name="location" value="classpath:views.xml"/>
    </bean>
    
</beans>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

views.xml,我们在其中配置一个View bean,bean的id为xvr,与Controller方法中设置的逻辑视图名一致!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!--配置一个InternalResourceView类型的View bean,name或者id属性需要与逻辑视图名匹配-->
    <bean id="xvr" class="org.springframework.web.servlet.view.InternalResourceView">
        <!--配置jsp或者其他资源的url路径,请求将对转发到指定的资源-->
        <property name="url" value="/WEB-INF/jsp/xvr.jsp"/>
    </bean>
</beans>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

/WEB-INF/jsp目录下配置一个jsp视图模版xvr.jsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>XmlViewResolver</title>
</head>
<body>
<h1>XmlViewResolver!</h1>
<br/>
<span style="color: #dc143c; ">${msg}</span>
<br/>
</body>
</html>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

项目总体结构如下:

在这里插入图片描述

启动项目,访问/xmlViewResolver,结果如下:

在这里插入图片描述

说明我们配置成功!

3 ResourceBundleViewResolver

ViewResolver的一种实现,继承了AbstractCachingViewResolver,具有缓存View视图的能力。

ResourceBundleViewResolver支持接受properties配置文件,配置文件的编写和XmlViewResolver的XML配置文件的编写有很大区别。properties中key的编写就是通过viewname.(class)指定View视图的实现类,ket的前缀就是逻辑视图名,通过viewname.propertyName指定某个View视图的属性。

ResourceBundleViewResolver将根据Controller处理器方法返回的逻辑视图名称到指定的properties配置文件中寻找对应名称的 View bean并返回,用于处理视图,这就是它的工作(将逻辑视图名解析为真正的View视图对象)原理!

默认配置文件是classpath下面的views.properties,如果不使用默认配置,那么可以在ResourceBundleViewResolver的basename或者basenames属性中指定,这里只能指定配置文件前缀,,如指定的baseName是base,那么base.properties、baseabc.properties等等以base开始的属性文件都会被Spring当做ResourceBundleViewResolver解析视图的资源文件。

默认最大缓存1024个,缓存的View的key为viewName和locale的组合:

在这里插入图片描述

3.1 使用案例

如下案例,一个Controller:

/**
 * @author lx
 */
@Controller
public class ResourceBundleViewResolverController {

    @RequestMapping("/resourceBundleViewResolver")
    public ModelAndView handle() {
        ModelAndView modelAndView = new ModelAndView();
        //添加模型数据
        modelAndView.addObject("msg", "ResourceBundleViewResolver测试");
        //添加逻辑视图名
        modelAndView.setViewName("rbvr");
        return modelAndView;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

spring-mvc-config.xml配置文件中我们手动注册了一个ResourceBundleViewResolver,basename前缀为view。

<!--手动配置一个ResourceBundleViewResolver-->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <!--指定配置文件前缀-->
    <property name="basename" value="view"/>
</bean>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在resources目录下添加一个views.properties,我们在其中配置一个View, id为rbvr,与Controller方法中设置的逻辑视图名一致!

#指定视图名rbvr以及通过(class)指定View所属类型
rbvr.(class)=org.springframework.web.servlet.view.InternalResourceView
#指定某个视图名的类型的属性,这里指定rbvr视图的url属性
rbvr.url=/WEB-INF/jsp/rbvr.jsp
 
  • 1
  • 2
  • 3
  • 4

在/WEB-INF/jsp目录下配置一个jsp视图模版rbvr.jsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>ResourceBundleViewResolver</title>
</head>
<body>
<h1>ResourceBundleViewResolver!</h1>
<br/>
<span style="color: #dc143c; ">${msg}</span>
<br/>
</body>
</html>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

启动项目,访问/xmlViewResolver,结果如下:

在这里插入图片描述

说明我们配置成功!

4 UrlBasedViewResolver

ViewSolver的简单实现,继承了AbstractCachingViewResolver,具有缓存View视图的能力。

UrlBasedViewResolver,提供了一种通过拼接前后缀的方式来方便的获取逻辑视图对应的真实物理视图URL的功能。我们可以通过配置prefix属性指定一个URL前缀,通过suffix属性指定一个URL后缀,然后把返回的逻辑视图名称加上指定的前缀和后缀就是指定的物理视图URL了。比如prefix设置为“/WEB-INF/pages/”,suffix设置为“.jsp”,返回的逻辑视图为“index”,那么最终的物理视图URL就是“/WEB-INF/pages/index.jsp”默认情况下。prefix和suffix都是空字符串,设置为null同样被解析为空串!

配置UrlBasedViewResolver时,必须指定一个viewClass属性,该UrlBasedViewResolver会将所有的逻辑视图名解析为该类型的View对象。由于从逻辑视图到物理视图(路径)的映射就是拼接URL的规则,并且解析出来的viewClass指定为一种,因此我们必须要像XmlViewResolver和ResourceBundleViewResolver那样手动配置View的映射以及实现,而是由UrlBasedViewResolver自动创建!

UrlBasedViewResolver将尝试检查逻辑视图对应的物理资源是否确实存在。但是,如果viewClass为InternalResourceView,则通常无法确定目标资源是否真实存在。在这种情况下,UrlBasedViewResolver将始终返回一个任何给定逻辑视图名称的InternalResourceView视图对象,无论它是否真实存在,永远不会返回null。因此,应将它配置为解析器链中的最后一个视图解析器(如果存在多个解析器),以尽量防止后续因物理视图资源找不到而返回的404响应!

同时,可以为UrlBasedViewResolver设置viewNames属性,这是一个String数组,表示该UrlBasedViewResolver能解析的逻辑视图名,支持通配符匹配,比如*,如果没有设置viewNames(将会是null),或者设置了但是没有匹配,那么resolveViewName将会返回null,进而由下一个ViewResolver解析。

XmlViewResolver指定的cachekey就是viewName:

在这里插入图片描述

4.1 使用案例

如下案例,一个Controller:

/**
 * @author lx
 */
@Controller
public class UrlBasedViewResolverController {

    @RequestMapping("/urlBasedViewResolver")
    public ModelAndView handle() {
        ModelAndView modelAndView = new ModelAndView();
        //添加模型数据
        modelAndView.addObject("msg", "UrlBasedViewResolver测试");
        //添加逻辑视图名
        modelAndView.setViewName("ubvr");
        return modelAndView;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在spring-mvc-config.xml配置文件中我们手动注册一个UrlBasedViewResolver。

<!--手动配置一个UrlBasedViewResolver-->
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <!--指定物理视图路径前缀-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--指定物理视图路径后缀-->
    <property name="suffix" value=".jsp"/>
    <!--必须指定解析的View视图的类型-->
    <!--这里指定为InternalResourceView,那么将会将请求转发到物理视图的路径-->
    <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>
</bean>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在/WEB-INF/jsp目录下配置一个jsp视图模版ubvr.jsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>UrlBasedViewResolver</title>
</head>
<body>
<h1>UrlBasedViewResolver!</h1>
<br/>
<span style="color: #dc143c; ">${msg}</span>
<br/>
</body>
</html>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

启动项目,访问/ urlBasedViewResolver,结果如下:

在这里插入图片描述

说明我们配置成功,并且它的配置更加简单!

5 InternalResourceViewResolver

专门用于支持内部资源视图InternalResourceView(实际上,就是为了支持Servlet和JSP资源)及其子类(如 JstlView 和TilesView)的 UrlBasedViewResolver 的便捷子类。

相比于父类UrlBasedViewResolver,不需要指定viewClass属性,默认的解析得到的视图类型就是InternalResourceView,InternalResourceView的一个特性就是它的会把Controller处理器方法返回的model模型属性都存放到对应的request中,然后通过RequestDispatcher在服务器端把请求forword转发到目标物理资源视图URL。基于InternalResourceView的隐匿的转发机制,对于访问/WEB-INF/下面的受保护的资源来说是一种非常方便快捷且安全的方式了。

UrlBasedViewResolver通过设置viewClass为InternalResourceView的类型,也可以达到同样的效果!同样的,如果存在解析器链,InternalResourceViewResolver始终需要放在最后一个,因为它将尝试解析任何给定的逻辑视图名称并返回一个对应的InternalResourceView,无论对应的资源是否确实存在。

5.1 使用案例

如下案例,一个Controller:

/**
 * @author lx
 */
@Controller
public class InternalResourceViewResolverController {

    @RequestMapping("/internalResourceViewResolver")
    public ModelAndView handle() {
        ModelAndView modelAndView = new ModelAndView();
        //添加模型数据
        modelAndView.addObject("msg", "InternalResourceViewResolver测试");
        //添加逻辑视图名
        modelAndView.setViewName("irvr");
        return modelAndView;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在spring-mvc-config.xml配置文件中我们手动注册一个InternalResourceViewResolver,注意此时我们应该将此前配置的UrlBasedViewResolver注释掉!

<!--手动配置一个InternalResourceViewResolver-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--指定物理视图路径前缀-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--指定物理视图路径后缀-->
    <property name="suffix" value=".jsp"/>
</bean>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在/WEB-INF/jsp目录下配置一个jsp视图模版irvr.jsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>InternalResourceViewResolver</title>
</head>
<body>
<h1>InternalResourceViewResolver!</h1>
<br/>
<span style="color: #dc143c; ">${msg}</span>
<br/>
</body>
</html>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

启动项目,访问/internalResourceViewResolver,结果如下:

在这里插入图片描述

说明我们配置成功!

6 FreeMarkerViewResolver

专门用于支持FreeMarkerView的 UrlBasedViewResolver 的便捷子类,和InternalResourceViewResolver比较类似,FreeMarkerViewResolver默认会将逻辑视图名解析为FreeMarkerView视图。

FreeMarkerViewResolver将检查指定模板资源是否存在,并且仅在实际找到模板时返回非null的 View 对象。

虽然View仅仅是进行了数据的准备和API的调用,但是由于JSP视图的渲染和数据填充是Servlet容器和tomcat服务器天然支持和实现的,因此不必引入专门的依赖,而对于FreeMarker模版的渲染和数据填充技术则需要引入专门的FreeMarker依赖,这样FreeMarkerView才能正常工作!

<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.30</version>
</dependency>
<!--用于引入FreeMarkerConfigurationFactory-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>5.2.8.RELEASE</version>
</dependency>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

6.1 使用案例

如下案例,一个Controller:

/**
 * @author lx
 */
@Controller
public class FreeMarkerViewResolverController {

    @RequestMapping("/freeMarkerViewResolver")
    public ModelAndView handle() {
        ModelAndView modelAndView = new ModelAndView();
        //添加模型数据
        modelAndView.addObject("msg", "FreeMarkerViewResolver测试");
        //添加逻辑视图名
        modelAndView.setViewName("fmvr");
        return modelAndView;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在spring-mvc-config.xml配置文件中我们手动注册一个FreeMarkerViewResolver,并且指定配置信息:

<!-- 手动配置一个FreeMarkerViewResolver,专门用于解析FreeMarker视图 -->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="suffix" value=".ftl"/>
    <property name="contentType" value="text/html;charset=UTF-8" />
</bean>

<!--freemarkerConfig-->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <!-- 配置freeMarker的模板路径 -->
    <property name="templateLoaderPath" value="/WEB-INF/ftl"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在/WEB-INF/ftl目录下配置一个freeMarker视图模版fmvr.ftl:

<!DOCTYPE html>
<html lang="zn_CH">
<head>
    <title>FreeMarkerViewResolver</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>FreeMarkerViewResolver!</h1>
<br/>
<span style="color: #dc143c; ">${msg}</span>
<br/>
</body>
</html>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

启动项目,访问/freeMarkerViewResolver,结果如下:

在这里插入图片描述

说明我们配置成功!实际上freeMarker很有很多的功能,这里仅仅演示了最基本的功能!

7 ContentNegotiatingViewResolver

基于请求文件名或Accept头解析视图的ViewResolver实现,又被称为内容协商视图解析器!通过ContentNegotiatingViewResolver可以实现“同一资源,多种表述”,即不同的请求,实际上都是请求同一个资源,但是返回的格式不一样!

实际上ContentNegotiatingViewResolver本身不会自行不解析视图View,而是委托给其他视图解析器,并选择一个客户端请求需要的View返回。ContentNegotiatingViewResolver通过将请求设置的媒体类型mediaTypes与ViewResolvers中的每一个ViewResolver关联的View支持的媒体类型(MediaType,也称为Content-Type)进行比较,从而选择合适的View处理该请求。

列表中具有兼容Content-Type的第一个View将选中并用于给客户端发送响应。如果视图解析器链无法提供兼容的视图,则将查询通过defaultViews属性指定的View视图列表,此选项适用于可以呈现当前资源的适当表示形式的单个视图,而不考虑逻辑视图名称。

7.1 获取MediaType

ContentNegotiatingViewResolver从请求中获取媒体类型(MediaType)的方式有四种,获取顺序依次为扩展名、请求参数、Accept Header、默认值!

最后可以获取到多个MediaType,也就是请求支持返回多种MediaType,将返回一个MediaType类型的requestedMediaTypes集合(getMediaTypes方法)。

7.1.1 扩展名

例如:

  1. /test.xml 对应的MediaType可能解析为application/xml;
  2. /test.json 对应的MediaType可能解析为application/json;
  3. /test.html 对应的MediaType可能解析为text/html;

从扩展名获取MediaType可以通过favorPathExtension属性(该属性在后续版本中可能移除)配置是否开启,默认是开启的。

7.1.2 请求参数

例如:

  1. /test?format=xml 对应的MediaType可能解析为application/xml;
  2. /test?format=json 对应的MediaType可能解析为application/json;
  3. /test?format=html 对应的MediaType可能解析为text/html;

从请求参数获取MediaType可以通过favorParameter属性配置是否开启,默认是关闭的。默认的参数名为format,可通过parameterName属性配置!
在的配置类中通过mediaTypes属性可以配置参数值和具体的MediaType的映射关系,扩展名和请求参数的方式解析时可以使用。如果开启了favorParameter,那么必须配置mediaTypes(除非配置useRegisteredExtensionsOnly=false),这么做的原因或许是为了提高隐私性,比如你可以配置参数值为“xx”,对应“text/html”。对于扩展名的方式,则无需配置mediaTypes,会自动从ServletContext.getMimeType和MediaTypeFactory中查找映射,且一般都能找到。

7.1.3 Accept Header

通过在Accept Header可以指定MediaType,比如可以htext/jsp、text/pdf、 text/xml、text/json、或者无Accept 请求头,Accept中可以指定通过“,”指定多个MediaType。还可以指定通配符(例如text/*),在这种情况下,其Content-Type为text/xml的View是兼容的匹配项。

Accept Header中的MediaType字符串将会被自动解析,无需配置映射关系!

可以通过ignoreAcceptHeader属性来设置是否忽略使用Accept Header来确定请求的媒体类型MediaType,默认false,即不忽略

7.1.4 默认值

通过defaultContentType属性配置,如果前面的方式无法找到该请求的MediaType,那么使用默认MediaType。默认为null,表示没有默认MediaType。

7.2 获取View

ContentNegotiatingViewResolver本身不会自行不解析View,最终返回的View要么是来源于有两个:

  1. 通过viewResolvers属性明确指定使用哪些ViewResolver来产生View,如果不配置viewResolvers,默认spring会查找所有其他的ViewResolver来产生View。
  2. 通过defaultViews属性配置的默认View列表。如果其他ViewResolver返回的View无法兼容指定的MediaType,才会查询通过defaultViews属性指定的View视图列表 。

由于要使用其他的所有ViewResolver,因此它的应该排在视图解析器的最前面,它的默认order为Ordered.HIGHEST_PRECEDENCE,也就是最小值 Integer.MIN_VALUE。

在通过全部的ViewResolver解析ViewName并返回View之后,会将返回的View加入到一个View集合candidateViews,并将defaultViews中的View整体加入到candidateViews集合尾部并返回(getCandidateViews方法)。

7.3 获取匹配的View

在获取到requestedMediaTypes和candidateViews集合之后,将会选出最匹配指定的MediaType的View并返回(通过getBestView方法)。

  1. 首先遍历candidateViews,如果某个candidateView属于SmartView类型,并且会执行重定向(即实际类型为RedirectView),那么直接返回该candidateView,方法结束。
  2. 随后遍历requestedMediaTypes集合,在内部为每一个mediaType遍历candidateViews集合,如果mediaType兼容某个candidateView的getContentType方法返回的MediaType,那么直接返回该candidateView,方法结束。

7.4 总体流程

ContentNegotiatingViewResolver的resolveViewName的总体流程如下,还是比较清晰的!

在这里插入图片描述

如果找不到合适的 View 对象,即View为null,那么:

  1. 如果将 useNotAcceptableStatusCode 设为 true,那就会响应406 Not Acceptable 错误。
  2. 如果设为 false(默认就是false),那就回传 null,表示交给解析器链中的下一个 View Resolver 处理。

8 默认ViewResolver

Spring 5.2.8.RELEASE版本中,默认情况下,将会注册org.springframework.web.servlet.view.InternalResourceViewResolver这一个视图解析器!如果指定了其他视图解析器,那么不会加载默认配置!

9 其他细节

9.1 视图解析器链

我们可以通过声明视图解析器 bean 来形成一个视图解析器链,如有必要,还可以通过设置 order 属性来指定顺序。order值越大,视图解析器在链中的位置越靠后。

ViewResolver的协定指定它的resolveViewName方法可以返回 null 表示当前视图解析器找不到视图,随后将会对解析器链中的下一个解析亲戚进行调用。然而,对于InternalResourceViewResolver,它将始终返回一个不为null的InternalResourceView视图对象,主要用于访问JSP视图,而确定是否真正存在JSP视图资源的唯一方法是通过RequestDispatcher进行调度。因此,我们必须始终将InternalResourceViewResolver配置为在视图解析器的总体顺序中排在最后,以尽量保证减少错误视图!

9.2 视图名特殊前缀

9.2.1 redirect:

可以通过指定逻辑视图名称中的“redirect:”前缀来便捷表示需要执行重定向操作,视图名称的后半部分是重定向的 URL路径。UrlBasedViewResolver(及其子类)将其为识别为需要重定向的指令,并将逻辑视图名解析为RedirectView,最终的结果与控制器直接返回RedirectView相同。

一个Controller:

/**
 * @author lx
 */
@Controller
public class RedirectController {

    @RequestMapping("/redirect1")
    public ModelAndView handle1() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:redirect/rd.jsp");
        return modelAndView;

    }

    @RequestMapping("/redirect2")
    public View handle2() {
        return new RedirectView("redirect/rd.jsp");
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在/webapp/redirect目录下新建一个rd.jsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Redirect</title>
</head>
<body>
<h1>Redirect!</h1>
</body>
</html>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

启动后访问/redirect1和/redirect2,发现请求URL都变了:

在这里插入图片描述

9.2.2 forward:

除了“redirect:”前缀,还可以使用“forward:”前缀,这表示UrlBasedViewResolver及其子类会将该逻辑视图名解析为InternalResourceView,最终会执行RequestDispatcher.forward()方法实现请求转发。

此前缀在InternalResourceViewResolver和InternalResourceView(对于JSP)中没有用,因为默认就是请求转发,但是如果您使用另一种视图技术但仍然希望强制转发由Servlet / JSP引擎处理的资源,则该前缀很有用。

10 对应MVC配置

除了定义bean的方式配置ViewResolver之外,我们可以通过mvc标签或者JavaConfig快速的配置需要的ViewResolver,对于常见的配置来说,mvc的配置简化了视图解析器的注册!

FreeMarker、Tiles、Groovy Markup、Script templates等模版同样也需要引入对应的基础视图技术,即相关基础依赖!

10.1 基于mvc标签的配置

基于XML标签配置式样如下,在<mvc:view-resolvers>中可以配置各种视图解析器!

<!--配置视图解析器-->
<mvc:view-resolvers>
    <!--配置一个InternalResourceViewResolver,主要用于解析JSP视图-->
    <!--默认前缀为"/WEB-INF/",默认后缀为".jsp"-->
    <mvc:jsp prefix="/WEB-INF/jsp" suffix=".jsp"/>

    <!--配置一个TilesViewResolver-->
    <mvc:tiles />

    <!--配置一个FreeMarkerViewResolver-->
    <mvc:freemarker />

    <!--配置一个BeanNameViewResolver-->
    <mvc:bean-name/>

    <!--配置一个ContentNegotiatingViewResolver-->
    <mvc:content-negotiation/>

    <!--配置一个GroovyMarkupViewResolver-->
    <mvc:groovy/>

    <!--配置一个GroovyMarkupViewResolver-->
    <mvc:script-template/>
</mvc:view-resolvers>
<!--freemarker模版配置-->
<mvc:freemarker-configurer/>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

10.2 基于JavaConfig的配置

实现WebMvcConfigurer并且实现configureViewResolvers方法!

/**
 * @author lx
 */
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //配置一个InternalResourceViewResolver
        registry.jsp("/WEB-INF/jsp", ".jsp");
        //配置一个TilesViewResolver
        registry.tiles();
        //配置一个FreeMarkerViewResolver
        registry.freeMarker();
        //配置一个BeanNameViewResolver
        registry.beanName();
        //配置一个ContentNegotiatingViewResolver
        registry.enableContentNegotiation();

        //………………
    }


    /**
     * FreeMarkerConfigurer模版配置
     */
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/freemarker");
        return configurer;
    }

    //………………其他配置
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

相关文章:

https://spring.io/

Spring Framework 5.x 学习

Spring Framework 5.x 源码