Spring MVC 支持的视图技术
Spring MVC 请求处理方法处理完成后,会返回一个 ModelAndView 对象,该对象包含了模型对象的信息,和视图逻辑名,再借助视图解析器(ViewResolver)得到最终的视图(View),该视图可以是一个 JSP,也可以是一个基于 FreeMarker、Velocity 模板技术的视图,或者XML,JSON,Excel,PDF等;
以下示例代码地址:
https://gitee.com/assad/springframework-test-mvc-extra
JSP和JSTL
Spring 借助 InternelResourceViewResolver 视图解析器解析 JSP 视图页面,在 spring-mvc上下文中的常用配置如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" />
Spring MVC 支持在 JSP 页面中使用 JSTL 标签,配合 EL 表达式可以很方便地进行流程控制,数据绑定,数据处理等,当用到<fmt:message>进行国际化输出时,需要更改 InternalResourceViewResoulver 的默认视图实现类为 JstlView:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:viewClass="org.springframework.web.servlet.view.JstlView"
p:prefix="/WEB-INF/views/"
p:suffix=".jsp" />
Spring 本身还提供了一套
表单标签<from>,可以很方便地将模型数据中的表单/命令对象绑定到 HTML 表单元素中,他们共有的属性如下:
path | 用属性路径表示的表单对象属性; |
htmlEscape | 绑定的表单属性值是是否要对 HTML 特殊字符进行转换,默认值为 true; |
cssClass | 表单组件对应的CSS样式类名 |
cssErrorClass | 当表单组件的数据存在相应的验证错误时,采用的 CSS 样式类 |
cssStyle | 表单组件对应的 CSS 样式串 |
其中的一些常用的表单标签如下:
<form:input> | 输入框标签组件,如 <form:input path=”userName” /> |
<form:password> | 密码框输入组件,如 <form:passowrd path=”password” /> |
<form:hidden> | 隐藏框组件,如 <form:input path=”userId” /> |
<form:textarea> | 多行输入框组件,如 <form:textarea path=”userNote” rows=”3″ cols=”20″ /> |
<form:radiobutton> | 单选框组件,当表单的对应属性和value值相同时,该单选框被选中,如: <form:radiobutton path=”sex” value=”0″ label=”男“/> <form:radiobutton path=”sex” value=”1″ label=”女”/> 当表单对象sex=0,第一个单选框被选中; |
<form:raidobuttons> | 单选框组件组,用于构造多个的单选框,如下: <form:radiobuttons path=”sex” items=”${sexOptions}” delimiter=”<br/>” /> sexOptions 可以在jsp中定义,该对象可以是一个List,String[],或Map,用于产生和该变量元素相同的单选框组件; 默认情况下,如果 sexOptions 为 List / String[] ,单选框的 value 和 label 相同; 如果 sexOptions 为 Map 类型,单选框的 value 为 Map 的 key,label 为 Map 的 value; 也可以通过 itemValue,itemLabel 来更改以上Map这个行为: |
<form:checkbox> | 复选框组件,如: <form:checkbox path=”like” value=”1″ /> <form:checkbox path=”like” value=”2″ /> 一般来说,like |
<form:checkboxs> | 复选框组件组,用于够着多个复选框,如下: <form:checkboxs path=”like” items=”${likeOptions}” delimeter=”<br/>”> 用法类似于<form:raidobuttons> |
<form:select> | 下拉框组件,如: <form:select path=”city” items=”${citys}”> <form:option value=”” label=”–请选择–“> <form:options items=”${cityMap}” itemValue=”key” itemLabel=”value” /> </form:select> 用法类似于<form:raidobuttons> |
<form:errors> | 显示表单错误校验信息,如: <form:errors path=”userName”/> 表示“userName”属性的错误 <form:errors path=”*/”> 表示绑定对象所有属性错误 <form:errors path=”user*”> 表示所有“user”前缀的属性的错误 |
示例使用代码:
<jsp:useBean id="user" class="site.assad.domain.User" scope="request" />
<form:form action="/user/handleRegister" method="post" modelAttribute="user">
用户名:<form:input path="userName" /><br/>
<form:errors path="userName" cssClass="error"/><br />
密码:<form:input path="password" /><br/>
<form:errors path="password"/><br />
Email:<form:input path="email" cssClass="error" /><br/>
<form:errors path="email"/><br />
生日(格式"yyyy-MM-dd"):<form:input path="birthday" /><br/>
<form:errors path="birthday" cssClass="error"/><br />
<input type="submit" value="提交" />
<input type="reset" value="重置" /><br/>
</form:form>
模板视图 FreeMarker,Velocity
Spring MVC 支持 FreeMarker ,Velocity 视图引擎,他们是基于模板生成文本输出地通用工具,FreeMarker 可以基于模板生成 HTML,XML,Java 源码等多种类型的输出内容,该内容可以是一段字符模板,也可以是一个文件;
以下示例在 Spring MVC 中使用 Freemarker 作为视图输出: 示例代码模块:
site.assad.web.UserController(控制器)
webapp/views/user/ftl/userListFtl.ftl(FreeMarker模板视图)
webapp/assad-servlet.xml(spring-mvc配置文件)
FreeMarker 视图模板 userListFtl.ftl 如下:
<html>
<head>
<title>User Lists</title>
</head>
<body>
<#list userList as user>
<p>user id:${user.userId}</p>
<p>user name:${user.userName}</p>
<p>user password:${user.password}</p>
<p>user birthday:${user.birthday?string("yyyy-MM-dd")}</p>
<br/>
</#list>
</body>
</html>
控制器
package site.assad.web;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//转发到 FreeMarker 模板视图
@RequestMapping("/showUserListByFtl")
public ModelAndView showUserListByFtl(){
List<User> userList = userService.getAllUser();
return new ModelAndView("/user/ftl/userListFtl","userList",userList);
}
}
在spring-mvc配置文件中配置 Freemarker 基础设施和解析器,assad-servlet.xml
<!--配置FreeMarker基础设施-->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
p:templateLoaderPath="/WEB-INF/views"
p:defaultEncoding="UTF-8">
<property name="freemarkerSettings">
<props>
<prop key="classic_compatible">true</prop>
</props>
</property>
</bean>
<!--配置FreeMarker解析器-->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
p:order="5"
p:suffix=".ftl"
p:contentType="text/html;charset=utf-8"
/>
同时 Spring 还提供了一系列的宏,特别是表单宏,类似于JSP中的标签,参考:
https://docs.spring.io/spring/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#views-form-macros
输出XML
Spring 支持将模型数据以 XML 的格式输出,对应的视图解析器为 MarshallingView ,以下是一个使用示例:
需要导入的依赖:
org.springframework:spring-oxm
com.thoughtworks.xstream:xstream:1.4.10(XStream,POJO与XML转换类)
在spring-mvc配置文件中添加如下:
assad-servlet.xml
<bean id="xmlMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="streamDriver">
<bean class="com.thoughtworks.xstream.io.xml.StaxDriver" />
</property>
<property name="annotatedClasses">
<list>
<value>site.assad.domain.User</value>
</list>
</property>
</bean>
<bean id="userListXML" class="org.springframework.web.servlet.view.xml.MarshallingView"
p:modelKey="userList"
p:marshaller-ref="xmlMarshaller"
/>
控制器类中的相关处理器代码:
UserController
package site.assad.web;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//转发到 XML 视图
@RequestMapping("/showUserListByXML")
public ModelAndView showUserListByXML(){
List<User> userList = userService.getAllUser();
return new ModelAndView("userListXML");
}
}
输出JSON
Spring 支持将模型数据以 JSON 的格式输出,对应的解析器为 MappingJackson2JsonView,即使用 Jackson 框架;
需要导入的依赖:
com.fasterxml.jackson.core:jackson-core
com.fasterxml.jackson.core:jackson-databind
在spring-mvc配置文件中添加如下:
assad-servlet.xml
<bean id="userListJSON" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"
p:modelKey="userList"
/>
控制器类中的相关处理器代码:
UserController
package site.assad.web;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//转发到 JSON 视图
@RequestMapping("/showUserListByJSON")
public ModelAndView showUserListByJSON(){
List<User> userList = userService.getAllUser();
return new ModelAndView("userListJSON");
}
}
输出Excel
Spring mvc 中可以借助 apache.poi API 来实现使用 Excel 作为视图,具体只需要拓展 Spring 的 AbstractExcelView 或 AbstractJExcelView 即可;
需要导入的依赖:
org.apache.poi:poi
实现 AbstractExcelView 拓展类:
package site.assad.web;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import site.assad.domain.User;
public class UserListExcelView extends AbstractExcelView {
@Override
protected void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
//excel 文件编码必须为“iso8859-1“,否则会出现乱码
response.setHeader("Content-Disposition","inline;filename=" + new String("用户列表".getBytes(),"iso8859-1"));
List<User> userList = (List<User>) model.get("userList");
//创建excel表格
HSSFSheet sheet = workbook.createSheet("users");
//创建行首
HSSFRow header = sheet.createRow(0);
header.createCell(0).setCellValue("账号");
header.createCell(1).setCellValue("用户名");
header.createCell(2).setCellValue("生日");
//填充模型
int rowNum = 1;
for(User user : userList){
HSSFRow row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(user.getUserId());
row.createCell(1).setCellValue(user.getUserName());
row.createCell(2).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(user.getBirthday()));
}
}
}
在spring-mvc配置文件中添加如下:
assad-servlet.xml
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="10" />
<bean id="userListExcel" class="site.assad.web.UserListExcelView" />
控制器类中的相关处理器代码:
UserController
//转发到 Excel 视图
@RequestMapping("/showUserListByExcel")
public ModelAndView showUserListByExcel(){
List<User> userList = userService.getAllUser();
return new ModelAndView("userListExcel");
}
输出PDF
Spring mvc 中实现PDF视图类似于 Excel,同样使用 BeanNameViewResolver 解析器;
需要导入的依赖:
com.lowagie:itext:2.1.7
itextasian-1.5.2.jar
※默认itext库是不支持亚洲文字(中文、日文、韩文等)的输出的,需要添加itextasian用于支持亚洲文字的输出;
实现 AbstractPdfView 拓展类:
package site.assad.web;
import com.lowagie.text.*;
import com.lowagie.text.Font;
import com.lowagie.text.pdf.BaseFont;
import org.springframework.web.servlet.view.document.AbstractPdfView;
import com.lowagie.text.pdf.PdfWriter;
import site.assad.domain.User;
public class UserListPDFView extends AbstractPdfView{
@Override
protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setHeader("Content-Disposition","inline;filename=" + new String("用户列表".getBytes(),"iso8859-1"));
List<User> userList =(List<User>) model.get("userList");
//创建表格,并设置表格格式
Table table = new Table(3);
table.setWidth(80); //设置表格宽度
table.setBorder(1); //设置表格边框
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER); //设置对齐格式
table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
//设置中文字体
BaseFont cnBaseFont = BaseFont.createFont("STSongStd-Light","UniGB-UCS2-H",false);
Font cnFont = new Font(cnBaseFont,10,Font.NORMAL, Color.BLUE);
//填充表头,中文字符需要使用转换器构造Cell,否则会产生乱码
table.addCell(buildFontCell("账号",cnFont));
table.addCell(buildFontCell("名称",cnFont));
table.addCell(buildFontCell("生日",cnFont));
//填充表格数据
for(User user : userList){
table.addCell(user.getUserId()+""); //英文字符直接添加
table.addCell(buildFontCell(user.getUserName(),cnFont));
table.addCell(new SimpleDateFormat("yyyy-MM-dd").format(user.getBirthday()));
}
}
private Cell buildFontCell(String content,Font font) throws RuntimeException{
try {
return new Cell(new Phrase(content,font));
} catch (BadElementException e) {
throw new RuntimeException(e);
}
}
}
在spring-mvc配置文件中添加如下:
assad-servlet.xml
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="10" />
<bean id="userListPDF" class="site.assad.web.UserListPDFView" />>
控制器类中的相关处理器代码:
UserController
//转发到 PDF 视图
@RequestMapping("/showUserListByPDF")
public ModelAndView showUserListByPDF(){
List<User> userList = userService.getAllUser();
return new ModelAndView("userListPDF");
}
混合多种视图技术
Spring 支持对于 REST 编程的风格的支持,REST 风格的应用对于资源的 URL 由严格的定义:一个资源对象对应一个 URL; Spring 提供了 ContentNegotiationManager 用于支持基于 REST 风格的混合资源请求视图;
假设希望使用以下 REST 风格的 URL 以不同的 MIME 格式获取相同的资源:
/user/showUserListMix:返回一个 HTML 页面显示的用户列表
/user/showUserListMix?content=xml:返回一个 XML 格式的用户列表
/user/showUserListMix?content=json:返回一个 JSON 格式的用户列表
控制器类中的相关处理器代码:
UserController
//使用同一URL获取不同格式的返回内容
@RequestMapping("/showUserListMix")
public ModelAndView showUserListMix(){
List<User> userList = userService.getAllUser();
return new ModelAndView("userListMix");
}
在spring-mvc配置文件中添加如下:
assad-servlet.xml
<!--配置 ContentNegontiatingVieqResolver -->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"
p:ignoreAcceptHeader="true"
p:favorPathExtension="false"
p:favorParameter="true"
p:parameterName="format"
p:defaultContentType="text/html" >
<!--不支持 Accept 报文头指定的 MIME 类型,仅通过请求参数指定 MIME 类型,参数名为content,以下为请求参数列表-->
<property name="mediaTypes">
<value>
html=text/html
xml=application/xml
json=application/json
</value>
</property>
</bean>
<!--配置混合视图解析器,并设置为最高优先级-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
p:order="0"
p:contentNegotiationManager-ref="contentNegotiationManager" >
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"
p:modelKey="userList" />
<bean class="org.springframework.web.servlet.view.xml.MarshallingView"
p:modelKey="userList" p:marshaller-ref="xmlMarshaller" />
</list>
</property>
</bean>
<!--配置jsp,xml,json 解析器,略,可以通过设置order属性来设定优先级-->
.......