视图和视图解析器
请求处理方法执行完成后,最终返回一个 ModelAndView 对象,对于那些返回 String、View 或 ModelMap 等类型的处理方法,SpringMVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了视图逻辑和模型对象的信息
SpringMVC 借助视图解析器 (ViewResolver) 得到最终的视图对象 (View),这可能是我们常见的 JSP 视图,也可能是一个基于 FreeMarker、Velocity 模本技术的视图,还可能是PDF、Excel、XML、JSON等各种形式的视图。
对于最终究竟采用何种视图对象对模型数据进行渲染,处理器并不关心,处理器的工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分耦合
认识视图
视图的作用是渲染模型数据,将模型数据里的数据以某种形式呈现给客户。视图对象可以是常见的 JSP,还可以是 Excel 或 PDF 等形式不一样的媒体形式。为了实现视图模型和具体实现技术的解耦,Spring在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口,该接口定义了两个方法:
1. String getContentType():视图对应的 MIME 类型,如 text/html、image/jpeg 等
2. void render(Map model, HttpServletRequest request, HttpServletResponse reponse):将模型数据以某种 MIME 类型渲染出来
视图对象是一个 Bean ,通常情况下,视图对象由视图解析器负责实例化。由于视图Bean是无状态的,所以它们不会有线程安全问题。
不同类型的视图实现技术对应不同的 View 实现类,这些实现类都位于 org.springframework.web.servlet.view 包中,常见的实现类如下
大类 |
视图类型 |
说明 |
URL 视图资源 |
InternalResourceView |
将JSP或其他资源封装成一个视图,是InternalResourceViewResolver默认使用的视图实现类 |
Jstliew |
如果JSP文件中使用了JSTL国际化标签的功能,则需要使用该视图类 |
|
文档视图 |
AbstractExcelView |
Excel文件视图的抽象类。该视图类基于POI构造Excel文档 |
AbstractPdfView |
PDF文档视图的抽象类。该视图类基于iText构造PDF文档 |
|
报表视图 |
ConfigurableJsperResportsView |
几个使用 JasperResports报表技术的视图 |
JasperReportsCsvView |
||
JasperResportsMultiFormatView |
||
JasperResportsHtmlView |
||
JasperResportsPdfView |
||
JasperResportsXlsView |
||
JSON视图 |
MappingJacksonJsonView |
将模型数据通过 Jackson开源框架的ObjectMapper以JSON方式输出 |
认识视图解析器
SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring Web 上下文配置一种或多种解析策略,并指定它们之间的先后顺序。每一种解析策略对应一个具体的视图解析器实现类,视图解析器的工作比较单一:将逻辑视图名解析为一个具体的视图对象。所有视图解析器都实现了 ViewResolver 接口,该接口仅有一个方法:
View resolveViewName(String viewName, Locale locale)
resolveViewName()的签名清楚地向我们传达了视图解析器工作的内涵:根据逻辑视图名和本地化对象得到一个视图对象。常用的视图解析器实现类如下表
大类 |
视图类型 |
说明 |
解析为Bean 的名字 |
BeanNameViewResolver |
将逻辑视图名解析为一个Bean,Bean 的id等于逻辑视图名 |
解析为URL文件 |
InternalResolverViewResolver |
将视图解析为一个URL文件,一般使用该解析器将视图名映射为一个保存在WEN-INF目录下的程序文件(如JSP) |
JasperResportsViewResolver |
JasperResports是一个基于Java的开源报表工具,该解析器将视图名解析为报表文件对应的URL |
|
模板文件 |
FreeMarkerViewResolver |
解析为基于FreeMarker模板技术的模板文件 |
VelocityViewResolver |
解析为基于Velocity模板技术的模板文件 |
|
VelocityLayoutViewResolver |
我们可以选择一种视图解析器或混用多种视图解析器。
每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高。
SpringMVC会按视图解析器的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常。
JSP是最常见的视图技术,可以使用 InternalResourceViewResolver 作为视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
若项目中使用了 JSTL,则SpringMVC 会自动把视图由 InternalResourceView 转为JstlView,若使用 JSTL 的 fmt 标签需要在 SpringMVC 的配置文件中配置国际化资源文件
<bean class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
若希望直接响应转发的页面,而无需在经过 Handler 的方法,可以使用 <mvc:view-controller> 标签实现
<mvc:view-controller path="/success" view-name="success"/>
在实际开发时通常需要配置 <mvc:annotation-driven> 标签来配合使用
<mvc:annotation-driven></mvc:annotation-driven>
我们还可以实现自己的视图,实现自己的视图需要实现 View 接口。新建一个 HelloView 类
package com.bupt.springmvc.views; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.View; @Component public class HelloView implements View { @Override public String getContentType() { return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.getWriter().print("hello view, time " + new Date()); } }
springDispatcherSerlet-servlet.xml 配置解析器,我们使用视图名字来解析视图,所以需要实例化视图类
<!-- 配置自动扫描的包 --> <context:component-scan base-package="com.bupt.springmvc"/> <!-- 配置视图 BeanNameViewResolver 解析器:使用视图名字来解析视图 --> <!-- 通过 order 属性来定义视图的优先级,order 值越小优先级越高 --> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> <property name="order" value="100"></property> </bean>
配置web.xml
<!-- 自定义的spring 配置文件可以放置在src下,名字在属性中指定 --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
创建 Handler 方法类
@RequestMapping("/springmvc") @Controller public class SpringTest { private static final String SUCCESS = "success" @RequestMapping("/testView") public String testView() { System.out.println("Test View"); return "helloView"; } }
定义一个index.jsp页面,编写一个超链接
<a href="springmvc/testView">Test View</a>
启动服务器,访问jsp页面点击超链接,可以看到程序跳转和控制台输出
重定向和转发
一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图处理,如果返回的字符串中带 forward: 或 redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward:和 redirect: 当成指示符,其后的字符串作为 URL 来处理
- redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作
- forward:success.jsp:会完成一个到 success.jsp 的转发操作
在SpringTest类中增加映射,同时在index.jsp中增加超链接
@RequestMapping("/testRedirect") public String testRedirect() { System.out.println("Test Redirect"); return "redirect:/index.jsp";
// return "forward:/index.jsp"; }
<a href="springmvc/testRedirect">Test Redirect</a>
当启动服务器点击超链接时,可以看到页面重新跳转回index.jsp页面
程序在执行重定向和转发操作时,会判断程序是以 redirect 还是 forward 作为前缀。如果以 redirect 作为前缀,则程序会在底层创建一个 RedirectView,若果是 forward 则会在低层创建一个 InternalResourceView。
forward 转发和 redirect 重定向,二者直观的区别就是:forward 时浏览器地址栏不会变,但是有视图返回回来;redirect 时地址栏会发生变化。
二者其实在整个请求处理的过程中存在本质的区别,具体可以查看 javaweb 关于转发和重定向方面的资料。