目录
Spring MVC视图解析器
视图解析器(ViewResolver)
Spring MVC中所有控制器的处理器方法都必须返回一个逻辑视图的名字,无论是显式返回(比如返回一个String、View或者ModelAndView)还是隐式返回(比如基于约定的返回)。Spring中的视图由一个视图名标识,并由视图解析器来渲染。Spring有非常多内置的视图解析器。
请求处理方法执行完成之后,最终返回一个ModelAndView对象。对于那些返回String等类型的处理方法,Spring MVC也会在内部将它们装配成一个ModelAndView对象,它包含了逻辑视图名和数据模型,那么此时Spring MVC就需要借助视图解析器(View Resolver)。
View Resolver是Spring MVC处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析成一个真正的视图对象。真正的视图对象可以多种多样(JSP、FreeMarker)。
针对不同的视图对象,使用不同的视图解析器来完成实例化工作。可以在Spring上下文配置多个视图解析器,并通过order属性来指定解析器优先级顺序,order越小,对应的ViewResolver将有越高的解析视图的权利。当一个 ViewResolver在进行视图解析后返回的View对象是null的话就表示该ViewResolver不能解析该视图,这时候就交给优先级更低的进行解析,直到解析工作完成,如果所有视图解析器都不能完成将解析,则会抛出异常。
Spring MVC提供了多种视图解析器,都实现了ViewResolver接口
Spring MVC用于处理视图最重要的两个接口是ViewResolver和View。ViewResolver的主要作用是把一个逻辑上的视图名称解析为一个真正的视图,SpringMVC中用于把View对象呈现给客户端的是View对象本身,而ViewResolver只是把逻辑视图名称解析为对象的View对象。View接口的主要作用是用于处理视图(渲染),然后返回给客户端。
1.面向单一视图类型的ViewResolver
该类别ViewResolver的正宗名称应该是UrlBasedViewResolver(视图解析器都直接地或者间接地继承自该类)。使用该类别的ViewResolver,不需要为它们配置具体的逻辑视图名到具体View的映射关系。通常只要指定一下视图模板所在的位置,这些ViewResolver就会按照逻辑视图名,抓取相应的模板文件、构造对应的View实例并返回。之所有又将它们称之为面向单一视图类型的ViewResolver,是因为该类别中,每个具体的ViewResolver实现都只负责一种View类型的映射,ViewResolver与View之间的关系是一比一。
比如:一直使用的InternalResourceViewResolver,它通常就只负责到指定位置抓取JSP模板文件,并构造InternalResourceView类型的View实例并返回。而VelocityViewResolver则只关心指定位置的Velocity模板文件(.vm),并会将逻辑视图名映射到视图模板的文件名,然后构造VelocityView类型的View实例返回,诸如此类。
该类型包含InternalResourceViewResolver、FreeMarkerViewResolver、XsltViewResolver这3个实现类。
2.面向多视图类型的ViewResolver
使用面向单一视图类型的ViewResolver,不需要指定明确的逻辑视图名与具体视图之间的映射关系,对应的ViewResolver将自动到指定位置匹配自己所管辖的那种视图模板,并构造具体的View实例。面向多视图类型的ViewResolver则不然。使用面向多视图类型的ViewResolver,需要通过某种配置方式明确指定逻辑视图名与具体视图之间的映射关系,这可能带来配置上的烦琐。不过,好处是,面向多视图类型的ViewResolver可以顾及多种视图类型的映射管理。如果逻辑视图名想要映射到InternalResourceView,那么面向多视图类型的ViewResolver可以做到。如果逻辑视图名想要映射到VelocityView,那么,面向多视图类型的ViewResolver也可以做到。相对于只支持单一视图类型映射的情况,面向多视图类型的ViewResolver更加灵活。
该类型包含ResourceBundleViewResolver、XmlViewResolver,BeanNameViewResolver这3个实现类。
内置视图解析器
(1)AbstractCachingViewResolver(抽象类)
public abstract class AbstractCachingViewResolver
extends WebApplicationObjectSupport
implements ViewResolver {}
一个抽象的视图解析器类,提供了缓存视图的功能。通常视图在能够被使用之前需要经过准备。继承这个基类的视图解析器即可以获得缓存视图的能力
AbstractCachingViewResolver是带有缓存的ViewResolver,它每次解析时先从缓存里查找,如果找到视图就返回,没有就创建新的视图,且创建新视图的方法由其子类实现。
(2)UrlBasedViewResolver
1.该类继承自AbstractCachingViewResolver抽象类、并实现Ordered接口的类,是ViewResolver接口简单的实现类
2.复写createView方法
3.父类(AbstractCachingViewResolver)的createView()内部会调用loadView抽象方法,本身实现了这个抽象方法
public class UrlBasedViewResolver
extends AbstractCachingViewResolver
implements Ordered {}
ViewResolver接口的一个简单实现。直接使用URL来解析到逻辑视图名,除此之外不需要其他任何显式的映射声明。如果逻辑视图名与真正的视图资源名是直接对应的,那么这种直接解析的方式就很方便,不需要再指定额外的映射。
UrlBasedViewResolver是对ViewResolver的一种简单实现,继承了AbstractCachingViewResolver,主要提供一种拼接URL的方式来解析视图,可以通过prefix属性指定一个前缀,通过suffix属性指定一个后缀,然后把返回的逻辑视图名称加上指定的前缀和后缀就是视图的URL,如果prefix=/WEB-INF/jsps/,suffix=.jsp,返回的视图名称viewName=test/indx,则UrlBasedViewResolver解析出来的视图URL就是/WEB-INF/jsps/test/index.jsp。默认的prefix和suffix都是空字符串。
(1)forword:前缀
URLBasedViewResolver还支持forword:前缀,对于视图名称中包含forword:前缀的视图名称将会被封装成一个InternalResourceView对象,然后在服务器端利用RequestDispatcher的forword方式跳转到指定的地址。使用UrlBasedViewResolver的时候必须指定属性viewClass,表示解析成哪种视图,一般使用较多的就是InternalResourceView,利用它来展现jsp,但是当我们使用JSTL的时候我们必须使用JstlView。
(2)redirect:前缀
URLBasedViewResolver支持返回的逻辑视图名称中包含redirect:前缀,这样就可以支持URL在客户端的跳转,如当返回的视图名称是”redirect:test.do”的时候,URLBasedViewResolver发现返回的视图名称包含”redirect:”前缀,于是把返回的视图名称前缀”redirect:”去掉,取后面的test.do组成一个RedirectView,RedirectView中将把请求返回的模型属性组合成查询参数的形式组合到redirect的URL后面,然后调用HttpServletResponse对象的sendRedirect方法进行重定向。
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="prefix" value="/WEB-INF/" />
<property name="suffix" value=".jsp" />
<property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>
</bean>
(3)XmlViewResolver
XmlViewResolver是XML视图解析器,实现了ViewResolver接口,接受相同DTD定义的XML配置文件作为Spring的XML bean工厂。继承自AbstractCachingViewResolver抽象类,所以它也是支持视图缓存的。通俗来说就是通过xml指定逻辑名称与真实视图间的关系,它从XML配置文件中查找视图实现(默认XML配置文件为/WEB-INF/views.xml)。
如果不使用默认值的时候可以在XmlViewResolver的location属性中指定它的位置。XmlViewResolver还实现了Ordered接口,因此可以通过其order属性来指定在ViewResolver链中它所处的位置,order的值越小优先级越高。
1.配置Bean
在Spring MVC的配置文件中加入XmlViewResolver的bean定义。使用location属性指定其配置文件所在的位置,order属性指定当有多个ViewResolver的时候其处理视图的优先级。
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location" value="/WEB-INF/views.xml"/>
<property name="order" value="1"/>
</bean>
2.创建XML配置视图解析器定义
在XmlViewResolver对应的配置文件中配置好所需要的视图定义
配置了一个名为internalResource的InternalResourceView,其url属性为“/index.jsp”
/WEB-INF/view.xml
<bean id="internalResource" class="org.springframework.web.servlet.view.InternalResourceView">
<property name="url" value="/index.jsp"/>
</bean>
3.配置Controller
定义一个返回的逻辑视图名称为在XmlViewResolver配置文件中定义的视图名称——internalResource。
@RequestMapping("/xmlViewResolver")
public String testXmlViewResolver() {
return "internalResource";
}
4.访问URL
当访问testXmlViewResolver处理器方法的时候返回的逻辑视图名称为“internalResource”,这时候Spring就会到定义好的views.xml中寻找id或name为“internalResource”的bean对象予以返回,这里Spring找到的是一个url为“/index.jsp”的InternalResourceView对象。
(4)BeanNameViewResolver
这个视图解析器跟XmlViewResolver有点类似,也是通过把返回的逻辑视图名称去匹配定义好的视图bean对象。将逻辑视图名解析为一个Bean,Bean的id等于逻辑视图名。
不同点有二
(1)BeanNameViewResolver要求视图bean对象都定义在Spring的application context中,而XmlViewResolver是在指定的配置文件中寻找视图bean对象
(2)BeanNameViewResolver不会进行视图缓存
在SpringMVC的配置文件中定义了一个BeanNameViewResolver视图解析器和一个id为test的InternalResourceview bean对象。
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="1"/>
</bean>
<bean id="test" class="org.springframework.web.servlet.view.InternalResourceView">
<property name="url" value="/index.jsp"/>
</bean>
这样当返回的逻辑视图名称是test的时候,就会解析为上面定义好id为test的InternalResourceView
(5)InternalResourceViewResolver(最常用)
最常用的视图解析器,通常用于查找JSP和JSTL等视图。
URLBasedViewResolver的子类,所以URLBasedViewResolver支持的特性它都支持。在实际应用中InternalResourceViewResolver也是使用的最广泛的一个视图解析器。
存放在/WEB-INF/下面的内容是不能直接通过request请求的方式请求到的,为了安全性考虑,通常会把jsp文件放在WEB-INF目录下,而InternalResourceView在服务器端跳转的方式可以很好的解决这个问题。
1.UrlBasedViewResolver的子类,将InternalResourceView作为缺省的View类,如果当前classpath中有jstl的jar包时则使用JstlView作为缺省的view来渲染结果。
2.InternalResourceViewResolver不管能不能解析它都不会返回null,也就是说它拦截了所有的逻辑视图,让后续的解析器得不到执行,所以InternalResourceViewResolver必须放在最后。
InternalResourceViewResolver会把返回的视图名称都默认解析为InternalResourceView对象,InternalResourceView会把Controller处理器方法返回的模型属性都存放到对应的request属性中,然后通过RequestDispatcher在服务器端把请求forword重定向到目标URL。
比如在InternalResourceViewResolver中定义了prefix=/WEB-INF/,suffix=.jsp,然后请求的Controller处理器方法返回的视图名称为test,那么这个时候InternalResourceViewResolver就会把test解析为一个InternalResourceView对象,先把返回的模型属性都存放到对应的HttpServletRequest属性中,然后利用RequestDispatcher在服务器端把请求forword到/WEB-INF/test.jsp。这就是InternalResourceViewResolver一个非常重要的特性。
在springmvc-servlet.xml配置如下
<!-- 完成视图对应 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
// 设置路径
<property name="prefix" value="/WEB-INF/jsp/" />
// 设置后缀
<property name="suffix" value=".jsp" />
// 设置编码utf-8
<property name="contentType" value="text/html;charset=UTF-8" />
// 设置优先级
<property name="order" value="10" />
</bean>
当处理器返回“index”时,InternalResourceViewResolver解析器会自动添加前缀和后缀:/WEB-INF/jsp/index.jsp
order表示视图解析的优先级,数目越小优先级越大(即:0为优先级最高,所以优先进行处理视图),InternalResourceViewResolver在项目中的优先级必须设置为最低,也就是order要最大。不然它会阻碍其他视图解析器。
@Bean // 配置JSP视图解析器
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setContentType("text/html;charset=UTF-8");
resolver.setOrder(10);
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
解析JSTL视图
如果在JSP中使用JSTL标签来处理格式化和信息的话,那么就会让InternalResourceViewResolver将视图解析为JstlView。JSTL的格式化标签需要一个Locale对象,以便于恰当地格式化地域相关的值,如日期和货币。信息标签可以借助Spring的信息资源和Locale,从而选择适当的信息渲染到HTML之中。通过解析 JstlView,JSTL能够获得Locale对象以及Spring中配置的信息资源
InternalResourceViewResolver默认是使用了InternalResourceView作为视图的实现类,如果你想使用JSTL标签的一些功能的话,例如使用这个,那就需要用JstlView来替换InternalResourceView
如果想让InternalResourceViewResolver将视图解析为JstlView,而不是InternalResourceView的话,那么只需设置它的viewClass属性即可
@Bean // 配置JSP视图解析器
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
InternalResourceViewResolver还提供下面设置
1.alwaysInclude属性来要求返回的结果使用include方式而不是forward方式
2.exposeContextBeansAsAttributes属性以将当前spring 环境中的
3.beans作为request attritbutes来暴露到页面上
4.exposedContextBeanNames属性来限制能够暴露到页面上的spring bean的名称列表
(6)ResourceBundleViewResolver
和XmlViewResolver一样,也继承AbstractCachingViewResolver,但是缓存的不是视图。和XmlViewResolver一样也需要有一个配置文件来定义逻辑视图名称和真正的View对象的对应关系。
根据proterties文件来找对应的视图来解析“逻辑视图”的,该properties文件默认是放在classpath路径下的views.properties文件。
指定配置文件
如果不使用默认值,则可以通过属性baseName或baseNames来指定。baseName只是指定一个基名称,Spring会在指定的classpath根目录下寻找以指定的baseName开始的属性文件进行View解析,如指定的baseName是base,那么base.properties、baseabc.properties等等,以base开始的属性文件都会被Spring当做ResourceBundleViewResolver解析视图的资源文件。
ResourceBundleViewResolver使用的属性配置文件的内容类似于这样
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
<property name="order" value="1"/>
</bean>
这时ResourceBundleViewResolver会从classpath路径下的views.properties文件中寻找物理视图
views.properties
login.(class)=org.springframework.web.servlet.view.JstView
login.url=/WEB-INF/jsp/login.jsp
register.(class)=org.springframework.web.servlet.view.InternalResourceView
register.url=/index.jsp
properties文件里面存放的是key-value数据,从文件中可以看出逻辑视图与视图Bean也是以这种方式绑定的
@RequestMapping(value="/page",method=RequestMethod.GET)
public String getPage(Model model){
return "login";
}
1.login/register表示处理器(Controller)返回的逻辑视图
2.login.(class)表示视图Bean对应的视图类
3.login.url表示物理视图
注意:要将这个“views.properties”文件放到项目的classpath路径下。
视图解析器链
Spring支持同时使用多个视图解析器。因此,可以配置一个解析器链,并做更多的事比如,在特定条件下覆写一个视图等。可以通过把多个视图解析器(ViewResolver)设置到应用上下文(application context)中的方式来串联它们,然后它们会组成一个ViewResolver链。如果需要指定次序,那么设置order属性即可,order属性是Integer类型。order属性的值越大(默认最大值),该视图解析器在链中的位置就越靠后(order值越小,先执行)
视图解析器链执行顺序
(1)当Controller处理器方法返回一个逻辑视图名称后,ViewResolver链将根据其中ViewResolver的优先级来进行处理
(2)当一个ViewResolver在进行视图解析后返回的View对象是null,就表示该ViewResolver不能解析该视图,这时候如果还存在其他order值比它大的ViewResolver就会调用剩余的,类推
(3)当ViewResolver在进行视图解析后返回的非空的View对象的时候,就表示该ViewResolver能够解析该视图,那么视图解析这一步就完成了,后续ViewResolver将不会再用来解析该视图
(4)当定义的所有ViewResolver都不能解析该视图的时候,Spring就会抛出一个异常(ServletException)
基于Spring支持的这种ViewResolver链模式,就可以在Spring MVC应用中同时定义多个ViewResolver,给定不同的order值,这样就可以对特定的视图特定处理,以此来支持同一应用中有多种视图类型
视图解析器的接口清楚声明了,一个视图解析器是可以返回null值的,这表示不能找到任何合适的视图。
注意:InternalResourceViewResolver能解析所有的视图,即永远能返回一个非空View对象,必须总是赋予最低的优先级(最大的order值),因为不管返回什么视图名称,它都将解析视图
如果它的优先级高于其它解析器的优先级的话,它将使得其它具有较低优先级的解析器没有机会解析视图
<!-- 模板视图解析器-->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="defaultEncoding" value="UTF-8" />
<property name="templateLoaderPath" value="/freemarker/" />
</bean>
<bean id="fmViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="contentType" value="text/html;charset=utf-8" />
<property name="cache" value="false" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<property name="order" value="1"></property>
</bean>
<!-- excel视图解析器-->
<bean id="rbViewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="excelConfig"/>
<property name="order" value="2"/>
</bean>
<!-- jsp视图解析器-->
<bean id="resourceView" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="order" value="3"/>
</bean>
视图解析器转发和重定向
控制器通常都会返回一个逻辑视图名,然后视图解析器会把它解析到一个具体的视图技术上去渲染
对于一些可以由Servlet或JSP引擎来处理的视图技术,比如JSP等。这个解析过程通常是由InternalResourceViewResolver和InternalResourceView协作来完成的,而这通常会调用Servlet的RequestDispatcher.forward(…)方法或RequestDispatcher.include(…)方法,并发生一次内部的转发(forward)或引用(include)。而对于其他的视图技术,比如Velocity、XSLT等,视图本身的内容是直接被写回响应流中的。
(1)转发前缀(forward:)
请求转发是一次请求,地址栏不会发生变化,区别于重定向。Spring MVC环境自行配置。
对于最终会被UrlBasedViewResolver或其子类解析的视图名,可以使用一个特殊的前缀:forward:。这会导致一个InternalResourceView视图对象的创建(最终会调用RequestDispatcher.forward()方法),后者会认为视图名剩下的部分是一个URL。
因此,这个前缀在使用InternalResourceViewResolver和InternalResourceView时并没有特别的作用(比如对于JSP来说)。但当主要使用的是其他的视图技术,而又想要强制把一个资源转发给Servlet/JSP引擎进行处理时,这个前缀可能就很有用(或者,也可能同时串联多个视图解析器);与redirect:前缀一样,如果控制器中的视图名使用了forward:前缀,控制器本身并不会发觉任何异常,它关注的仍然只是如何处理响应的问题。
1.请求转发传统的方式(原生Servlet)
前端页面
<a href="${pageContext.request.contextPath}/forwardCommon">请求转发经过视图解析器</a>
后台代码
public void forwardCommon(HttpServletRequest request,HttpServletResponse response){
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
}
@RequestMapping(“/forwardCommon”)
2.请求转发经过视图解析器
前端页面
<a href="${pageContext.request.contextPath}/forwardMvcView">请求转发经过视图解析器</a>
后台代码
@RequestMapping("/forwardMvcView")
public String forwardMvcView(){
return "success";
}
3.请求转发不经过视图解析器
前端页面
<a href="${pageContext.request.contextPath}/forwardView">请求转发经过视图解析器</a>
后台代码
由于不经过视图解析器,所以需要自己拼接前后缀)forward:关键字后面的路径表示不再经过视图解析器
@RequestMapping("/forwardView")
public String forwardView() {
return "forward:/WEB_INF/pages/success.jsp";
}
(2)重定向前缀(redirect:)
尽管使用RedirectView来做重定向能工作得很好,但如果控制器自身还是需要创建一个RedirectView,那无疑控制器还是了解重定向这么一件事情的发生。这还是有点不尽完美,不同范畴的耦合还是太强。控制器其实不应该去关心响应会如何被渲染。一般来说,它应该只对注入到其中的视图名起作用。
一个特别的视图名前缀能完成这个解耦:redirect:。如果返回的视图名中含有redirect:前缀,那么UrlBasedViewResolver(及它的所有子类)就会接受到这个信号,意识到这里需要发生重定向。然后视图名剩下的部分会被解析成重定向URL。
这种方式与通过控制器返回一个重定向视图RedirectView所达到的效果是一样的,不过这样一来控制器就可以只专注于处理并返回逻辑视图名了。如果逻辑视图名是这样的形式:redirect:/myapp/some/resource,他们重定向路径将以Servlet上下文作为相对路径进行查找,而逻辑视图名如果是这样的形式:redirect:http://myhost.com/some/arbitrary/path,那么重定向URL使用的就是绝对路径
注意:如果控制器方法注解了@ResponseStatus,那么注解设置的状态码值会覆盖RedirectView设置的响应状态码值
有时,想要在视图渲染之前,先把一个HTTP重定向请求发送回客户端
(1)当一个控制器成功地接受到了POST过来的数据,而响应仅仅是委托另一个控制器来处理(比如一次成功的表单提交)时,希望发生一次重定向。在这种场景下,如果只是简单地使用内部转发,那么意味着下一个控制器也能看到这次POST请求携带的数据,这可能导致一些潜在的问题,比如可能会与其他期望的数据混淆等
(2)在渲染视图前对请求进行重定向。防止用户多次提交表单的数据。此时若使用重定向,则浏览器会先发送第一个POST请求;请求被处理后浏览器会收到一个重定向响应,然后浏览器直接被重定向到一个不同的URL,最后浏览器会使用重定向响应中携带的URL发起一次GET请求。因此,从浏览器的角度看,当前所见的页面并不是POST请求的结果,而是一次GET请求的结果。这就防止了用户因刷新等原因意外地提交了多次同样的数据。此时刷新会重新GET一次结果页,而不是把同样的POST数据再发送一遍
重定向视图Redirect View
强制重定向的一种方法是,在控制器中创建并返回一个Spring重定向视图RedirectView的实例。它会使得DispatcherServlet放弃使用一般的视图解析机制,因为已经返回一个(重定向)视图给DispatcherServlet了,所以它会构造一个视图来满足渲染的需求。紧接着RedirectView会调用HttpServletResponse.sendRedirect()方法,发送一个HTTP重定向响应给客户端浏览器
如果决定返回RedirectView,并且这个视图实例是由控制器内部创建出来的,那更推荐在外部配置重定向URL然后注入到控制器中来,而不是写在控制器里面。这样它就可以与视图名一起在配置文件中配置
@RquestMapping(value = "files/{path}", method = RequestMethod.POST)
public String processForm( HttpServletRequest request,
HttpServletResponse response,
LoginForm loginForm,
BindingResult result,
ModelMap model) {
//response.sendRedirect("http://www.yahoo.com");
//return "redirect:files/{path}";
String redirectUrl = "yahoo.com";
return "redirect:" + redirectUrl;
}
绝对路径重定向,但需要http://或https://开头
@RquestMapping(method = RequestMethod.POST)
public String processForm( HttpServletRequest request,
LoginForm loginForm,
BindingResult result,
ModelMap model) {
String redirectUrl = "http://www.yahoo.com";
return "redirect:" + redirectUrl;
}
使用RedirectView
@RequestMapping("/value")
public RedirectView localRedirect() {
RedirectView redirectView = new RedirectView();
redirectView.setUrl("http://www.google.com");
return redirectView;
}
使用ResponseEntity
@RquestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<Void> redirectToExternalUrl() throws URISyntaxException {
URI uri = new URI("http://www.google.com");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(uri);
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
}
向重定并传递数据
1.拼接字符串
模型中的所有属性默认都会考虑作为URI模板变量被添加到重定向URL中。剩下的其他属性,如果是基本类型或者基本类型的集合或数组,那它们将被自动添加到URL的查询参数中去。
return "redirect:/page/second?param1=lay¶m2=lay2";
2.采用RedirectAttribute来传参数
如果model是专门为该重定向所准备的,那么把所有基本类型的属性添加到查询参数中可能是我们期望那个的结果。但是,在包含注解的控制器中,model可能包含了专门作为渲染用途的属性(比如一个下拉列表的字段值等)。为了避免把这样的属性也暴露在URL中,@RequestMapping方法可以声明一个RedirectAttributes类型的方法参数,用它来指定专门供重定向视图RedirectView取用的属性。如果重定向成功发生,那么RedirectAttributes对象中的内容就会被使用;否则则使用模型model中的数据。
public String first(RedirectAttributes redirectAttribute){
redirectAttribute.addAttribute("param1", "lay");
return "redirect:/page/second";
}
3.RedirectAttributes#addFlashAttribute()用法
另外一种向重定向目标传递数据的方法是通过闪存属性(Flash Attributes)。与其他重定向属性不同,flash属性是存储在HTTP session中的(因此不会出现在URL中)
@Controller
@RequestMapping("/page")
public class RedirectDemo {
@RequestMapping("/first")
public String first(RedirectAttributes redirectAttribute){
redirectAttribute.addFlashAttibute("param1", "lay");
return "redirect:/page/second";
}
@RequestMapping("/second")
public String second(@ModelAttribute("param1") String param1){
System.out.println(param1);
return "second";
}
}
闪存属性
Flash属性(flash attributes)提供了一个请求为另一个请求存储有用属性的方法。这在重定向的时候最常使用,比如常见的POST/REDIRECT/GET模式。Flash属性会在重定向前被暂时地保存起来(通常保存在session中),重定向后会重新被下一个请求取用并立即从原保存地移除。
为支持flash属性,Spring MVC提供了两个抽象。FlashMap被用来存储flash属性,而用FlashMapManager来存储、取回、管理FlashMap的实例。
对flash属性的支持默认是启用的,并不需要显式声明,不过没用到它时它绝不会主动地去创建HTTP会话(session)。对于每个请求,框架都会“传进”一个FlashMap,里面存储了从上个请求(如果有)保存下来的属性;同时,每个请求也会“输出”一个FlashMap,里面保存了要给下个请求使用的属性。两个FlashMap实例在Spring MVC应用中的任何地点都可以通过RequestContextUtils工具类的静态方法取得控制器通常不需要直接接触FlashMap。一般是通过@RequestMapping方法去接受一个RedirectAttributes类型的参数,然后直接地往其中添加flash属性。通过RedirectAttributes对象添加进去的flash属性会自动被填充到请求的“输出”FlashMap对象中去。类似地,重定向后“传进”的FlashMap属性也会自动被添加到服务重定向URL的控制器参数Model中去。
匹配请求所使用的flash属性
flash属性的概念在其他许多的Web框架中也存在,并且实践证明有时可能会导致并发上的问题。这是因为从定义上讲,flash属性保存的时间是到下个请求接收到之前。问题在于,“下一个”请求不一定刚好就是你要重定向到的那个请求,它有可能是其他的异步请求(比如polling请求或者资源请求等)。这会导致flash属性在到达真正的目标请求前就被移除了。
为了减少这个问题发生的可能性,重定向视图RedirectView会自动为一个FlashMap实例记录其目标重定向URL的路径和查询参数。然后,默认的FlashMapManager会在为请求查找其该“传进”的FlashMap时,匹配这些信息。
这并不能完全解决重定向的并发问题,但极大程度地减少了这种可能性,因为它可以从重定向URL已有的信息中来做匹配。因此,一般只有在重定向的场景下,才推荐使用flash属性。
内容商议解析器
ContentNegotiatingViewResolver并不会解析视图,而是委托给其他的视图解析器去处理。
ContentNegotiatingViewResolver并没有解决视图本身,而是其他视图解析器的代表,选择类似于客户机所要求的表示的视图。
客户端需要两个策略来请求服务器的表示:
对于每个资源使用一个不同的URI,通常是使用URI中的不同文件扩展。
例如:URI < http://www.example.com/users/fred.pdf >请求用户fred的PDF表示< http://www.example.com/users/fred.xml >请求XML表示。
使用相同的URI让客户机定位资源,但是设置Accept HTTP请求头,以列出它所理解的媒体类型。例如,一个HTTP请求的HTTP请求< http://www.example.com/users/fred >和一个接受的标题设置到应用程序/ pdf请求用户fred的pdf表示,同时< http://www.example.com/users/fred >有一个接受者设置到文本/ xml请求xml表示。这种策略被称为内容谈判
出于这个原因,在开发基于浏览器的web应用程序时,使用一个不同的URI是很常见的。
为了支持资源的多个表示,Spring提供了content协商viewresolverto基于文件扩展或接受HTTP请求的头来解决视图。contentconveringviewresolver并没有执行视图解决程序本身,而是通过bean属性ViewResolvers指定的视图resolvers列表
content协商解析器选择一个适当的视图来处理请求,通过将请求媒体类型(s)与媒体类型(也称为内容类型)进行比较,以与每个ViewResolvers相关联的视图支持。在列表中有一个兼容内容-类型的第一个视图返回客户机的表示。如果一个兼容的视图不能由ViewResolver链提供,那么将通过DefaultViews属性指定的视图列表。后一种选项适用于单例视图,该视图可以呈现当前资源的适当表示,而不考虑逻辑视图名称。Accept头可能包括野卡,例如文本/ *,在这种情况下,一个视图的内容类型是文本/ xml是一种兼容的匹配。
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</list>
</property>
</bean>
<bean id="content" class="com.foo.samples.rest.SampleContentAtomView"/>
视图(View)
主要是渲染模型数据,整合Web资源,将模型里的数据以特定形式响应给客户端。
为了实现视图模型和具体实现技术的解耦,Spring在org.springframework.web.servlet包中定义了一个高度抽象的View接口
视图对象由视图解析器负责实例化。由于视图是无状态的,所以不会有线程安全的问题
请求处理方法执行完成后,最终返回一个ModelAndView对象。对于那些返回String,View 或ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个ModelAndView对象;包含了逻辑名和模型对象的视图
Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP ,也可能是Excel、JFreeChart 等各种表现形式的视图
对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC 的充分解耦