SpringMVC 视图 详解

来源:互联网 发布:小米电视3s 知乎 编辑:程序博客网 时间:2024/05/18 18:15
请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String、View 或 ModelMap 等类型的处理方法,Spring MVC 会在内部将它们装配成一个 ModelAndView 对象,它包含视图逻辑名和模型对象的信息
SpringMVC 借助视图解析器 (ViewResolver) 得到最终的视图对象 (View),该视图可以是 JSP、基于FreeMarker、Velocity模版技术的视图、PDF、Excel、XML、JSON 等各种形式的视图

不同视图实现类


视图解析器实现类


逻辑视图解析为 URI 资源


InternalResourceViewResolver 默认使用 InternalResourceView 作为视图实现类,如 JSP页面使用 JSTL 的<fmt:message/> 等标签时,用户需要使用 JstlView 替换默认的视图实现类
<!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver" p:order="100"
     p:viewClass="org.springframework.web.servlet.view.JstlView"  p:prefix="/WEB-INF/jsp/"  p:suffix=".jsp"/>

FreeMarker 和 Velocity
SpringMVC 可以将 FreeMarker、Velocity 作为视图,使用模版中的数据进行替换工作
在 spring-mvc.xml 中
<!-- FreeMarker基础设施及视图解析器配置 -->
<beanclass="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
     p:templateLoaderPath="/WEB-INF/ftl"p:defaultEncoding="UTF-8">
    <propertyname="freemarkerSettings">
        <props>
            <propkey="classic_compatible">true</prop><!-- 若不设置该属性,若碰到值为null的对象属性时,将抛出异常 -->
       </props>
    </property>
</bean>
<beanclass="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
     p:order="5"p:suffix=".ftl"p:contentType="text/html; charset=utf-8"/>

order 指定视图解析器的优先级,它将优先于 InternalResourceViewResolver 执行 (因为 InternalResourceViewResolver 默认优先级最低)。

@RequestMapping(value ="/showUserListByFtl")
publicString showUserListInFtl(ModelMap mm) {
    Calendar calendar =new GregorianCalendar();
    List<User> userList =new ArrayList<User>();
    User user1 =new User();
    user1.setUserName("tom");
    user1.setRealName("汤姆");
    calendar.set(1980,1,1);
    user1.setBirthday(calendar.getTime());
    User user2 =new User();
    user2.setUserName("john");
    user2.setRealName("约翰");
    user2.setBirthday(calendar.getTime());
    userList.add(user1);
    userList.add(user2);
    mm.addAttribute("userList", userList);
    return "userListFtl";
}

这样 "userListFtl" 的视图名将解析为 "/WEB-INF/ftl/userListFtl.ftl" 的视图对象

userListFtl.ftl 文件
<#import"spring.ftl"asspring/><!-- 引入Spring的FreeMarker宏定义文件 -->
<html>
   <head>
      <title><@spring.message"website.title"/></title>
   </head>
   <body>
      <@spring.message"user.userList.title"/><!-- 引入国际化资源 -->
     <table>
           <#list userListas user>
           <tr>
              <td>
                 <ahref="<@spring.url'/user/showUser/${user.userName}.html'/>">${user.userName}</a>
              </td>
                <td>${user.realName}</td>
                <td>${user.birthday?string("yyyy-MM-dd")}</td>   
            </tr>
           </#list>
      <table>
   </body>
</html>

Spring 为 FreeMarker 提供的宏



Excel 和PDF
若希望使用 Excel 展示用户列表,仅需要扩展 Spring 的 AbstractExcelView 或 AbstractJExcelView 即可。实现 buildExcelDocument()方法,在方法中使用
PDF 需要扩展 AbstractPdfView
具体步骤 :
1> 配置视图解析器
<!-- Excel及PDF视图解析器配置 -->
<beanclass="org.springframework.web.servlet.view.BeanNameViewResolver"p:order="10"/>
<beanid="userListExcel"class="com.baobaotao.web.UserListExcelView"/>
<beanid="userListPdf"class="com.baobaotao.web.UserListPdfView"/>

在 spring-mvc.xml 中
<!-- Excel及PDF视图解析器配置 -->
<beanclass="org.springframework.web.servlet.view.BeanNameViewResolver"p:order="10"/>
<beanid="userListExcel"class="com.baobaotao.web.UserListExcelView"/>
<beanid="userListPdf"class="com.baobaotao.web.UserListPdfView"/>

Excel 处理方法
importjava.util.List;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.apache.commons.lang.time.DateFormatUtils;
importorg.apache.poi.hssf.usermodel.HSSFRow;
importorg.apache.poi.hssf.usermodel.HSSFSheet;
importorg.apache.poi.hssf.usermodel.HSSFWorkbook;
importorg.springframework.stereotype.Component;
importorg.springframework.web.servlet.view.document.AbstractExcelView;
public classUserListExcelView extends AbstractExcelView {
   @Override
  protected voidbuildExcelDocument(Map<String, Object> model,
         HSSFWorkbook workbook, 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");
      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.getUserName());
         row.createCell(1).setCellValue(user.getRealName());
         String createDate = DateFormatUtils.format(user.getBirthday(),
              "yyyy-MM-dd");
         row.createCell(2).setCellValue(createDate);
      }
   }
}

PDF 处理方法
importjava.awt.Color;
importjava.util.List;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.apache.commons.lang.time.DateFormatUtils;
importorg.springframework.web.servlet.view.document.AbstractPdfView;
importcom.lowagie.text.Cell;
importcom.lowagie.text.Document;
importcom.lowagie.text.Element;
importcom.lowagie.text.Font;
importcom.lowagie.text.Phrase;
importcom.lowagie.text.Table;
importcom.lowagie.text.pdf.BaseFont;
importcom.lowagie.text.pdf.PdfWriter;
public classUserListPdfView extends AbstractPdfView {
    @Override
   protected voidbuildPdfDocument(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.getUserName());// 英文字符可直接添加到 Cell 中
           table.addCell(buildFontCell(user.getRealName(), cnFont));
            String createDate = DateFormatUtils.format(user.getBirthday(), "yyyy-MM-dd");
            table.addCell(createDate);
        }
        document.add(table);
    }
    private Cell buildFontCell(String content, Font font)throws RuntimeException {
        try {
            Phrase phrase =new Phrase(content, font);
            return new Cell(phrase);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

在 Controller 中
@RequestMapping(value ="/showUserListByXls")
publicString showUserListInExcel(ModelMap mm) {
    Calendar calendar =new GregorianCalendar();
    List<User> userList =new ArrayList<User>();
    User user1 =new User();
    user1.setUserName("tom");
    user1.setRealName("汤姆");
    calendar.set(1980,1,1);
    user1.setBirthday(calendar.getTime());
    User user2 =new User();
    user2.setUserName("john");
    user2.setRealName("约翰");
    user2.setBirthday(calendar.getTime());
    userList.add(user1);
    userList.add(user2);
    mm.addAttribute("userList", userList);
    return "userListExcel";
}
@RequestMapping(value ="/showUserListByPdf")
publicString showUserListInPdf(ModelMap mm) {
    Calendar calendar =new GregorianCalendar();
    List<User> userList =new ArrayList<User>();
    User user1 =new User();
    user1.setUserName("tom");
    user1.setRealName("汤姆");
    calendar.set(1980,1,1);
    user1.setBirthday(calendar.getTime());
    User user2 =new User();
    user2.setUserName("john");
    user2.setRealName("约翰");
    user2.setBirthday(calendar.getTime());
    userList.add(user1);
    userList.add(user2);
    mm.addAttribute("userList", userList);
    return "userListPdf";
}

需要引入的 Maven 包配置
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.7</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>

XML
Spring MVC 可以将模型中的数据以 xml 的形式输出,其对应的视图对象为 MarshallingView。MarshallingView 使用 Marshaller 将模型数据转换为 XML,通过 marshaller 属性注入到一个 MarshallingView 实例。默认情况下,MarshallingView 会将模型中的所有属性转换为 XML,由于模型属性中包涵许多隐式数据,直接将模型中的所有数据输出一帮情况这种结果不是预期结果,可通过 modelKey 指定模型中的哪个属性需要输出为 xml

在 spring-mvc.xml 中
<beanid="xmlMarshaller"class="org.springframework.oxm.xstream.XStreamMarshaller">
    <propertyname="streamDriver">
        <beanclass="com.thoughtworks.xstream.io.xml.StaxDriver"/>
    </property>
    <propertyname="annotatedClasses">
        <list>
            <value>com.baobaotao.domain.User</value>
        </list>
    </property>
</bean>
<beanid="userListXml"
     class="org.springframework.web.servlet.view.xml.MarshallingView"
     p:modelKey="userList"p:marshaller-ref="xmlMarshaller"/>

在 Controller 中
@RequestMapping(value ="/showUserListByXml")
publicString showUserListInXml(ModelMap mm) {
    Calendar calendar =new GregorianCalendar();
    List<User> userList =new ArrayList<User>();
    User user1 =new User();
    user1.setUserName("tom");
    user1.setRealName("汤姆");
    calendar.set(1980,1,1);
    user1.setBirthday(calendar.getTime());
    User user2 =new User();
    user2.setUserName("john");
    user2.setRealName("约翰");
    user2.setBirthday(calendar.getTime());
    userList.add(user1);
    userList.add(user2);
    mm.addAttribute("userList", userList);
    return "userListXml";
}

JSON
Spring MVC 的 MappingJacksonJsonView 借助 Jsckson 框架的 ObjectMapper 将模型数据转换为 JSON 格式输出。默认情况下,MappingJacksonJsonView 会将模型中所有数据输出为 JSON,可通过 renderedAttributes 指定模型中哪些属性需要输出

在 spring-mvc.xml 中
<beanid="userListJson"
     class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"
     p:renderedAttributes="userList"/>

在 Controller 中
@RequestMapping(value ="/showUserListByJson")
publicString showUserListInJson(ModelMap mm) {
    Calendar calendar =new GregorianCalendar();
    List<User> userList =new ArrayList<User>();
    User user1 =new User();
    user1.setUserName("tom");
    user1.setRealName("汤姆");
    calendar.set(1980,1,1);
    user1.setBirthday(calendar.getTime());
    User user2 =new User();
    user2.setUserName("john");
    user2.setRealName("约翰");
    user2.setBirthday(calendar.getTime());
    userList.add(user1);
    userList.add(user2);
    mm.addAttribute("userList", userList);
    return "userListJson";
}

使用 XmlViewResolver
若视图对象 Bean 数目太多,可以通过 XmlViewResolver 将视图文件独立在一个 xml 文件中,例如如下配置 :
<!-- XML文件或国际化资源文件定义视图 -->
<beanclass="org.springframework.web.servlet.view.XmlViewResolver"
     p:order="20"p:location="/WEB-INF/views/baobaotao-views.xml"/>
默认情况下,XmlViewResolver 在 WEB-INF/views.xml 中查找视图 Bean 的定义文件,文件中的 bean 和普通的 Spring配置文件没有区别
例如如下代码 :
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
   xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <beanid="userListJson1" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" p:renderedAttributes="message"/>
    <beanid="userListExcel1"class="com.baobaotao.web.UserListExcelView"/>
    <beanid="userListPdf1"class="com.baobaotao.web.UserListPdfView"/>      
</beans>

混合使用多种视图
Spring 中支持 Rest 编程风格,Rest 风格的应用对资源的 URL 有严格的要求 : 一个资源对象对应唯一的 URL
若想同一路径对应一个不同的视图,可以使用 HeepMessageConverter 对标注 @ResponseBody 或返回值为 ResponseEntity 的处理器方法进行响应信息转换的内容,Spring MVC 可以根据请求报文头的 Accept属性选择合适的 HttpMessageConverter 将处理方法的返回值以 XML、JSON等不同的形式输出响应。也就是说,调用者可以通过设置请求报文头 Accept 的值控制服务端返回的数据格式,实现对同一资源采用相同 URL 的 REST 编程风格。但是基于 HeepMessageConverter 的实现方式存在以下限制 :
1> 只能通过请求报头的 Accept 的值控制服务端返回的数据格式,如果客户端是浏览器,除非使用 Ajax,否则很难控制 Accept 报文头的值,一般情况下,这个值有浏览器决定
2> 无法通过 URL 扩展名或请求参数控制服务端的资源输出形式,因此无法将其对应 URL 发不出去
3> 如果希望 XML、JSON、一个网页等形式输出资源,HeepMessageConverter 很难达到要求,因为 HeepMessageConverter 很难调用一个视图对象渲染模型,直接负责将资源输出为某一内容形式

如果希望将资源以 XML、JSON等纯数据的格式输出,且不在意使用报文头控制资源输出,那么合适选择 HeepMessageConverter 的实现方式。否则,建议采用 SpringMVC 的 ContentNegotiatingViewResolver 试图解析器,它和 HeepMessageConverter 功能上有些重叠,但 ContentNegotiatingViewResolver 更加灵活

ContentNegotiatingViewResolver 可以根据请求信息上下文选择一个合适的视图解析器负责解析。一般将 ContentNegotiatingViewResolver 的优先级设置为最高,以保证其优先调用


原创粉丝点击