SpringMVC 视图 详解

请求处理方法执行完成后,最终返回一个 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及字符串解析为具体的页面 -->
< bean class ="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基础设施及视图解析器配置 -->
< bean class ="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
      p :templateLoaderPath ="/WEB-INF/ftl" p :defaultEncoding ="UTF-8" >
    < property name ="freemarkerSettings" >
        < props >
            < prop key ="classic_compatible" > true </ prop > <!-- 若不设置该属性,若碰到值为null的对象属性时,将抛出异常 -->
        </ props >
    </ property >
</ bean >
< bean class ="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" )
public String 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" as spring /> <!-- 引入Spring的FreeMarker宏定义文件 -->
< html >
   < head >
      < title > <@ spring . message "website.title" /> </ title >
   </ head >
   < body >
      <@ spring . message "user.userList.title" /> <!-- 引入国际化资源 -->
      < table >
           <#list userList as user >
            < tr >
               < td >
                  < a href= " <@ 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视图解析器配置 -->
< bean class ="org.springframework.web.servlet.view.BeanNameViewResolver" p :order ="10" />
< bean id ="userListExcel" class ="com.baobaotao.web.UserListExcelView" />
< bean id ="userListPdf" class ="com.baobaotao.web.UserListPdfView" />

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

Excel 处理方法
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.document.AbstractExcelView;
public class UserListExcelView extends AbstractExcelView {
   @Override
   protected void buildExcelDocument(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 处理方法
import java.awt.Color;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.web.servlet.view.document.AbstractPdfView;
import com.lowagie.text.Cell;
import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Phrase;
import com.lowagie.text.Table;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfWriter;
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.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" )
public String 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" )
public String 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 中
< 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 > com.baobaotao.domain.User </ value >
        </ list >
    </ property >
</ bean >
< bean id ="userListXml"
      class ="org.springframework.web.servlet.view.xml.MarshallingView"
      p :modelKey ="userList" p :marshaller-ref ="xmlMarshaller" />

在 Controller 中
@RequestMapping(value = "/showUserListByXml" )
public String 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 中
< bean id ="userListJson"
      class ="org.springframework.web.servlet.view.json.MappingJacksonJsonView"
      p :renderedAttributes ="userList" />

在 Controller 中
@RequestMapping(value = "/showUserListByJson" )
public String 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文件或国际化资源文件定义视图 -->
< bean class ="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" ?>
   xsi :schemaLocation =" http://www.springframework.org/schema/beans
   < bean id ="userListJson1"   class ="org.springframework.web.servlet.view.json.MappingJacksonJsonView"   p :renderedAttributes ="message" />
    < bean id ="userListExcel1" class ="com.baobaotao.web.UserListExcelView" />
    < bean id ="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 的优先级设置为最高,以保证其优先调用


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值