Spring MVC详解

来源:互联网 发布:淘宝上送的优酷会员 编辑:程序博客网 时间:2024/06/06 16:38
一、Spring概述
1. 简介

    Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。Spring框架在框架设计、扩展性、灵活性等方面全面超越了Struts、WebWork等MVC框架,从原来的追赶者一跃为MVC的领跑者。Spring框架围绕DispacherServlet这个核心展开,它负责截获请求并将其分派给相应的处理器处理。

2. Spring MVC和Struts2的比较
(1)Spring MVC的入口是servlet,而Struts2是filter,这里要指出filter和servlet是不同的。
(2)Spring MVC会稍微比Struts2快。Spring MVC是基于方法的设计,方法级别的拦截。而struts2设计是基于类,采用类级别的拦截。
(3)参数传递方面,Struts2用属性的get方法接受参数,这就说明参数是让多个方法共享的。
(4)配置文件方面很明显Struts2要复杂的多。

二、Spring MVC示例
1. 在web.xml中配置DispatcherServlet

    DispatcherServlet是Spring MVC的灵魂和心脏,它负责接收HTTP请求并协调Spring MVC的各个组件完成请求处理的工作。和任何Servlet一样,用户必须在web.xml中配置好DispatcherServlet。如下:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns="http://java.sun.com/xml/ns/j2ee"  
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.          xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee  
  5.          http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"  
  6.          version="2.4">  
  7.  <!-- 指定业务层Spring容器的配置文件,多个用逗号隔开 -->  
  8.     <context-param>  
  9.         <param-name>contextConfigLocation</param-name>  
  10.         <param-value>classpath:config/spring-config.xml</param-value>  
  11.     </context-param>  
  12.      
  13.     <!-- 通过contextConfigLocation指定的配置文件启动业务层的Spring容器 -->  
  14.     <listener>  
  15.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  16.     </listener>  
  17.  <!-- 配置名为spring的DispatcherServlet,默认自动加载/WEB-INF/spring-servlet.xml的配置文件,启动Web层的Spring容器。  
  18.  一个web.xml可以配置多个DispatcherServlet -->  
  19.  <!-- 这里,WEB层Spring容器将作为业务层Spring容器的子容器,即WEB层容器可以引用业务层容器的Bean,而业务层却不能访问WEB层容器的Bean -->  
  20.      <servlet>  
  21.         <servlet-name>spring</servlet-name>  
  22.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  23.         <!-- 指定使用类路径下的WEB层spring容器配置文件 -->  
  24.         <init-param>  
  25.             <param-name>contextConfigLocation</param-name>  
  26.             <param-value>classpath:config/spring-mvc.xml</param-value>  
  27.         </init-param>  
  28.         <load-on-startup>1</load-on-startup>  
  29.     </servlet>  
  30.  <!-- 指定DispatcherServlet处理所有URL以.html为后缀的HTTP请求 -->  
  31.     <servlet-mapping>  
  32.         <servlet-name>spring</servlet-name>  
  33.         <url-pattern>*.html</url-pattern>  
  34.     </servlet-mapping>  
  35. </web-app>  


2. 编写处理请求的控制器
    Spring MVC通过一个@Controller注解即可将一个POJO转化为处理器请求的控制器,通过@RequestMapping为控制器指定处理哪些URL的请求。如下:

UserController.java:
Java代码  收藏代码
  1. package com.jiang.web.controller;  
  2. import org.springframework.stereotype.Controller;  
  3. import org.springframework.web.bind.annotation.RequestMapping;  
  4. import org.springframework.web.bind.annotation.RequestMethod;  
  5. import org.springframework.web.servlet.ModelAndView;  
  6. import com.jiang.web.entity.User;  
  7. //标注UserController成为处理请求的控制器  
  8. @Controller  
  9. //处理来自/user URI的请求,限定请求路径为/user下,此路径可以不加  
  10. @RequestMapping("/user")  
  11. public class UserController {  
  12.     //直接处理“/user.html”的请求,请求的方法必须为POST  
  13.     @RequestMapping(method = RequestMethod.POST)  
  14.     //将表单值映射到User对象中  
  15.     public ModelAndView cteateUser(User user) {  
  16.         //注意此处的ModelAndView是在org.springframework.web.servlet包下,不要import错了  
  17.         ModelAndView mav = new ModelAndView();  
  18.         mav.setViewName("menu");  
  19.         mav.addObject("user", user);  
  20.         return mav;  
  21.     }  
  22.     //将处理http://127.0.0.1:8080/WalletManager/user/login.html的请求  
  23.     @RequestMapping("/login")  
  24.     public String index() {  
  25.         //返回一个逻辑视图名,根据配置文件映射为/WEB/INF/customer/login.jsp  
  26.         return "login";  
  27.     }  
  28. }  


User.java:
Java代码  收藏代码
  1. package com.jiang.web.entity;  
  2. public class User {  
  3.     private String userName;  
  4.     private String password;  
  5.     public String getUserName() {  
  6.         return userName;  
  7.     }  
  8.     public void setUserName(String userName) {  
  9.         this.userName = userName;  
  10.     }  
  11.     public String getPassword() {  
  12.         return password;  
  13.     }  
  14.     public void setPassword(String password) {  
  15.         this.password = password;  
  16.     }  
  17. }  


3. 编写视图对象
WEB-INF/customer/login.jsp:
Java代码  收藏代码
  1. <%@ page language="java" contentType="text/html; charset=utf-8"  
  2.  pageEncoding="utf-8"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  4. <html>  
  5. <head>  
  6. <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">  
  7. <title>登陆</title>  
  8. </head>  
  9. <body>  
  10.  <!-- 将表单提交到/user.html控制器中, -->  
  11.  <form method="post" action="/user.html">  
  12.   <table>  
  13.    <tr>  
  14.     <td>用户名:</td>  
  15.     <td><input type="text" name="userName" /></td>  
  16.    </tr>  
  17.    <tr>  
  18.     <td>密码:</td>  
  19.     <td><input type="password" name="password" /></td>  
  20.    </tr>  
  21.    <tr>  
  22.     <td><input type="submit" value="登陆" /></td>  
  23.    </tr>  
  24.   </table>  
  25.  </form>  
  26. </body>  
  27. </html>  


WEB-INF/customer/menu.jsp:
Java代码  收藏代码
  1. <%@ page language="java" contentType="text/html; charset=utf-8"  
  2.     pageEncoding="utf-8"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  4. <html>  
  5. <head>  
  6.  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">  
  7.  <title>新会员中心</title>  
  8. </head>  
  9. <body>  
  10.  Wellcome! ${user.userName}  
  11. </body>  
  12. </html>  


4. 配置Spring MVC的配置文件
spring-config.xml:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xmlns:context="http://www.springframework.org/schema/context"  
  5.        xmlns:util="http://www.springframework.org/schema/util"  
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  7.           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8.        http://www.springframework.org/schema/context  
  9.        http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">  
  10. </beans>  


spring-mvc.xml:
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xmlns:context="http://www.springframework.org/schema/context"  
  5.     xmlns:mvc="http://www.springframework.org/schema/mvc"  
  6.     xsi:schemaLocation="  
  7.            http://www.springframework.org/schema/beans  
  8.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  9.            http://www.springframework.org/schema/context  
  10.            http://www.springframework.org/schema/context/spring-context-3.0.xsd  
  11.            http://www.springframework.org/schema/mvc  
  12.            http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  13.      
  14.     <!-- 让Spring扫描包下标注注解的类生效 -->  
  15.     <context:component-scan base-package="com.jiang.web.controller"/>  
  16.        
  17.     <!-- 定义一个视图名称解析器,将名称解析为/WEB-INF/customer/名称.jsp -->  
  18.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  19.      <property name="prefix" value="/WEB-INF/customer/"></property>  
  20.      <property name="suffix" value=".jsp"></property>  
  21.     </bean>  
  22. </beans>  


5. SpringMVC处理user.html过程描述
(1)DispatcherServlet接收到客户端的user.html的请求
(2)DispatcherServlet使用DefaultAnnotationHandlerMapping查找负责处理该请求的处理器为user.html
(3)DispatcherServlet将请求分发给名为user.html的UserController处理器
(4)处理器完成业务处理后,返回ModelAndView对象,其中View的逻辑名为menu,模型包含一个键为user的User对象
(5)DispatcherServlet调用InternalResourceViewResolver组件对ModelAndView中的逻辑视图名进行解析,得到真实的/WEB-INF/customer/menu.jsp视图对象
(6)DispatcherServlet使用/WEB-INF/customer/menu.jsp对模型中的user模型对象进行渲染
(7)返回响应页面给客户端

三、注解驱动的控制器
1. 使用@RequestMapping映射请求

    在POJO类定义处标注@Controller,再通过<context:component-scan base-package="com.jiang.web.controller"/>扫描相应的类包,即可使POJO成为一个能处理HTTP请求的控制器。然后在控制器的类定义及方法定义处都可标注@RequestMapping,类定义处提供初步请求映射信息,方法处提供进一步的细分映射信息。
    将请求映射到控制器处理方法的工作包含一系列的映射规则,这些规则是根据请求中的各种信息制定的,具体包括请求URL、请求参数、请求方法、请求头这4个方面的信息项。

(1)通过请求URL进行映射
    @RequestMapping("/user"),它不但支持标准的URL,还支持Ant风格(即?、*和**的字符)的和带{xxx}占位符的URL。如下:
    /user/*/createUser:匹配/user/aaa/createUser、/user/bbb/createUser等URL
    /user/**/createUser:匹配/user/createUser、/user/aaa/bbb/createUser等URL
    /user/createUser??:匹配/user/createUseraaa、/user/createUserbbb等URL
    /user/{userId}:匹配/user/123、/user/234等URL
    /user/**/{userId}:匹配/user/aaa/bbb/123等URL
    /company/{companyId}/user/{userId}/detail:匹配/company/123/user/456/detail等URL

    URL中的{xxx}可以通过@PathVariable将URL中的占位符参数绑定到控制器处理方法的入参中,类和方法处的占位符都可以绑定到处理方法的入参中,如下:
Java代码  收藏代码
  1. @RequestMapping("owners/{ownerId}")  
  2. public class UserController{  
  3.   @RequestMapping("users/{userId}")  
  4.   public ModelAndView showDetail(@PathVariable("ownerId")String ownerId,@PathVariable("userId")String userId){  
  5.     ..  
  6.   }  
  7. }  

    注意,要使用上面的入参成功绑定URL中的占位符,必须在编译时开启debug开关。

(2)通过请求参数、请求方法或请求头进行映射让请求映射更加精确化



    @RequestMapping的value、method、params及headers分别表示请求URL、请求方法、请求参数及报文头的映射条件,它们之间是与的关系,联合使用多个条件项可让请求映射更加精确化。
Java代码  收藏代码
  1. @Controller  
  2. @RequestMapping("/user")  
  3. public class UserController {  
  4.     @RequestMapping(value="/delete",method = RequestMethod.POST,params="userId")  
  5.     public String test1(@PathVariable("userId")String userId) {  
  6.         return "user/test1";  
  7.     }  
  8.     @RequestMapping(value="/show",headers="content-type=text/*")  
  9.     public String test2(@PathVariable("userId")String userId) {  
  10.         return "user/test2";  
  11.     }  
  12. }  


2. 请求处理方法的签名
    处理方法的签名包括对方法及方法入参标注相应的注解、入参、返回值等。对于这些签名,SpringMVC会优雅的将Http请求的信息绑定到相应的方法入参中,并根据方法返回值类型作出相应的后续处理。

(1)使用@RequestParam绑定请求参数值
    @RequestMapping(value="/handle")
    public String handle(
        @RequestParam(value="userName",required=false)String userName,
        @RequestParam(value="age")int age) {
        ...
    }
    上面的实例将“UserName”和“age”请求参数分别绑定到handle()方法的userName和age中,并自动完成类型转换。required参数表示请求中是否必须包含对应的参数,默认为true,上面的实例如果不存在age请求参数,将抛出异常。还有一个defaultValue参数设置默认参数名,不推荐使用该参数。

(2)使用@CookieValue绑定请求中的Cookie值
    @RequestMapping(value="/handle")
    public String handle(
        @CookieValue(value="sessionId",required=false)String sessionId,
        @RequestParam(value="age")int age) {
        ...
    }
    value指定Cookie的名称,required为false表示请求中没有相应的Cookie时也不会报错。

(3)使用@RequestHeader绑定请求报文头的属性值
    @RequestMapping(value="/handle")
    public String handle(
        @RequestHeader("Accept-Encoding")String encoding,
        @RequestHeader("Keep-Alive")long keepAlive) {
        ...
    }
    参数和@RequestParam相同。

(4)使用命令/表单对象绑定请求参数值
    @RequestMapping(value="/handle")
    public String handle(User user) {
        ...
    }
    所谓命令/表单对象并不需要实现任何接口,仅是一个拥有若干属性的POJO,Spring MVC会按请求参数名和命令/表单对象属性名匹配的方式,自动对该对象填充属性值。支持级联的属性名,如address.addressName。

(5)使用Servlet API对象作为入参
    在Spring MVC中,控制器类可以不依赖任何Servlet API对象,但是Spring MVC并不阻止我们使用Servlet API的类作为处理方法的入参。

    @RequestMapping(value="/handle")
    public void handle(HttpServletRequest request,HttpServletResponse response) {
        ...//同时使用HttpServletRequest和HttpServletResponse作为入参
           //使用HttpServletResponse返回响应时,将处理方法返回值设成void即可
           String userName = WebUtils.findParameterValue(request,"userName");
           response.addCookie(new Cookie("userName", userName));
    }

    @RequestMapping(value="/handle")
    public String handle(HttpServletRequest request) {
        ...//只使用HttpServletRequest作为入参,可返回String或ModelAndView
    }

    @RequestMapping(value="/handle")
    public String handle(HttpSession session) {
        ...//使用HttpSession作为入参,可返回String或ModelAndView
    }

    @RequestMapping(value="/handle")
    public String handle(HttpServletRequest request, @RequestParam(value="userName",required=false)String userName) {
        ...//同时使用HttpServletRequest和基本类型作为入参,它们之间的位置不固定
    }

    Spring MVC在org.springframework.web.context.request包中定义了若干个可代理Servlet原生API类的接口,它们也可以作为处理类的入参,通过这些类可访问请求对象的任何信息。
    @RequestMapping(value="/handle")
    public String handle(WebRequest request) {
        ...//使用Spring MVC的WebRequest作为入参
    }

(6)使用IO对象作为入参
    Servlet的ServletRequest拥有getInputStream()和getReader()方法,可以通过它们读取请求的信息。相应Servlet的ServletResponse拥有getOutputStream()和getWriter()方法,可以通过它们输出响应信息。
    Spring MVC允许控制器的处理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream/java.io.Writer作为方法的入参,Spring MVC将获取ServletRequest的InputStream/Reader或ServletResponse的OutputStream/Writer,然后传递给控制器的处理方法。如下:

    @RequestMapping(value="/handle")
    public String handle(OutputStream os) throws IOException {
        Resource res = new ClassPathResource("/image.jpg");
        FileCopyUtils.copy(res.getInputStream(), os);//将图片写到输出流
    }

(7)其它类型的参数
    控制器处理方法的入参除支持以上类型的参数以外,还支持java.util.Locale、java.security.Principal,可以通过Servlet的HttpServletRequest的getLocal()及getUserPrincipal()得到相应的值。如果处理方法的入参类型为Locale或Principal,Spring MVC自动从请求对象中获取相应的对象并传递给处理方法的入参。

3. 使用HttpMessageConverter<T>
    HttpMessageConverter<T>是Spring3.0新添加的一个重要接口,它负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息。
    DispatcherServlet默认已经安装了AnnotationMethodHandlerAdapter作为HandlerAdapter的组件实现类,HttpMessageConConverter即由AnnotationMethodHandlerAdapter使用,将请求信息转换为对象,或将对象转换为响应信息。
   
    Spring为HttpMessageConverter<T>提供了众多的实现类,它们组成了一个功能强大、用途广泛的HttpMessageConverter<T>家族,如下:
        StringHttpMessageConverter    将请求信息转换为字符串,默认装配;
        FormHttpMessageConverter    将表单数据读取到MultiValueMap中;
        XmlAwareFormHttpMessageConverter    扩展于FormHttpMessageConverter,如果部分表单属性是XML数据,可用该转换器进行读取,默认装配;
        ResourceHttpMessageConverter    读写org.springframework.core.io.Resource类型;
        BufferedImageHttpMessageConverter    读写BufferedImage对象;
        ByteArrayHttpMessageconverter    读写二进制数据,默认装配;
        SourceHttpMessageConverter    读写javax.xml.transform.Source类型的数据,默认装配;
        MarshallingHttpMessageConverter    通过Spring的org.springframework.oxm.Marshaller(将Java对象转换成XML)和Unmarshaller(将XML解析为Java对象)读写XML消息;
        Jaxb2RootElementHttpMessageConverter     通过JAXB2读写XML消息,将请求消息转换到标注XmlRootElement和XmlType注解类型中;
        MappingJacksonHttpMessageConverter      通过利用Jackson开源类包的ObjectMapper读写JSON数据;
        RssChannerHttpMessageConverter    能够读写RSS种子消息;
        AtomFeedHttpMessageConverter    和RssChannerHttpMessageConverter能够读写RSS种子消息;

    上面标明默认装配的实现类已装配到AnnotationMethodHandlerAdapter,如果需要装配其他类型,可以Spring的的Web容器上下文中自行定义一个AnnotationMethodHandlerAdapter,如下:
        <!-- 定义一个AnnotationMethodHandlerAdapter,显式定义后将覆盖默认的AnnotationMethodHandlerAdapter -->
        <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" p:messageConverters-ref="messageConverters"/>
        <util:list id="messageConverters">
            <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
            .....
        </util:list>

    使用HtpMessageConverter<T>,将请求信息转化并绑定到处理方法的入参中。
(1)使用@RequestBody / @ResponseBody对处理方法进行标注(p540)
(2)使用HttpEntity<T> / ResponseEntity<T>作为处理方法的入参或返回值(p542)
       
示例:处理JSON
(1)首先为AnnotationMethodHandlerAdapter装配上可处理JSON的HttpMessageConverter:
Java代码  收藏代码
  1.    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
  2.        <property name="messageConverters">  
  3.            <list >  
  4.                <ref bean="mappingJacksonHttpMessageConverter" />  
  5.            </list>  
  6.        </property>  
  7.    </bean>  
  8.     
  9.    <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">  
  10.     <property name="supportedMediaTypes">  
  11.         <list>  
  12.             <value>application/json; charset=UTF-8</value>  
  13.             <value>text/html; charset=UTF-8</value>  
  14.        </list>  
  15.     </property>  
  16. </bean>   


(2)在控制器编写相应的方法:
   
Java代码  收藏代码
  1. @RequestMapping("/product")  
  2.     @ResponseBody  
  3.     public List<Product> findProductList() {  
  4.         List<Product> liProduct= new ArrayList<Product>();  
  5.         for (ProductCodeEnum t : ProductCodeEnum.values())  
  6.         {  
  7.          Product product = new Product();  
  8.             product.setId(t.getCode());  
  9.             product.setText(t.getCnName());  
  10.             product.setLeaf(true);  
  11.              
  12.             liProduct.add(product);  
  13.         }  
  14.         return liProduct;  
  15.     }  


    注意,处理JSON必须引入下面四个包:
 
Java代码  收藏代码
  1. <dependency>   
  2.    <groupId>org.codehaus.jackson</groupId>   
  3.       <artifactId>jackson-core-lgpl</artifactId>   
  4.       <version>1.9.13</version>   
  5.   </dependency>   
  6.   <dependency>   
  7.    <groupId>org.codehaus.jackson</groupId>   
  8.       <artifactId>jackson-core-asl</artifactId>   
  9.       <version>1.9.13</version>   
  10.   </dependency>   
  11.   <dependency>   
  12.   <groupId>org.codehaus.jackson</groupId>   
  13.       <artifactId>jackson-mapper-asl</artifactId>   
  14.       <version>1.9.13</version>   
  15.   </dependency>   
  16.   <dependency>   
  17.       <groupId>org.codehaus.jackson</groupId>   
  18.       <artifactId>jackson-mapper-lgpl</artifactId>   
  19.       <version>1.9.13</version>   
  20.   </dependency>   


4. 处理模型数据
    对于MVC框架来说模型数据是最重要的,因为控制层(C)是为了产生模型数据(M),而视图(V)则是为了渲染模型数据。
(1)ModelAndView
    控制器处理方法的返回值如果为ModelAndView,则其既包含视图信息,也包含模型数据信息。可以简单地将模型数据看成一个Map<Stirng, Object>对象。ModelAndView操作如下:
        ModelAndView addObject(String attributeName, Object attribute Value);  //添加模型数据
        ModelAndView addAllObjects(Map<String, ?> modelMap);  //添加模型数据
        void setView(View view);  //指定一个具体的视图对象
        void setViewName(String viewName);  //指定一个逻辑视图名

(2)ModelAttribute
    如果希望将方法入参对象添加到模型中,只需在相应入参前使用@ModelAttribute注解或在方法定义处使用@ModelAttribute注解即可。
        @RequestMapping(value = "/handle61")
        // SpringMVC将请求消息绑定到User对象中,然后再以“user”为键将User对象放到模型中。
        public String handle61(@ModelAttribute("user") User user) {
            user.setUserId("1000");
            return "/user/createSuccess";
        }

        @ModelAttribute("user")
        public User getUser() {
            User user = new User();
            user.setUserId("1001");
            user.setUserName("<>");
            return user;
        }
    当访问此控制器类中的任何一个请求处理方法前,都会事先执行标注了@ModelAttribute的getUser()方法,并将其返回值以user为键添加到模型中。

(3)Map及Model
    入参为org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map时处理方法返回时,Map中的数据会自动添加到模型中。
        @RequestMapping(value = "/handle63")
        public String handle63(ModelMap modelMap) {
            User user = (User) modelMap.get("user");
            user.setUserName("tom");
            modelMap.addAttribute("testAttr", "value1");
            return "/user/showUser";
        }

(4)SessionAttributes
    将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性
        // 将handle71处的模型属性自动保存到HttpSession中
        @SessionAttributes("user")

        @RequestMapping(value = "/handle71")
        public String handle71(@ModelAttribute("user") User user) {
            user.setUserName("John");
            return "redirect:handle72.html";  //向handle72发起一个新的请求
        }
        @RequestMapping(value = "/handle72")
        public String handle72(ModelMap modelMap, SessionStatus sessionStatus) {
            User user = (User) modelMap.get("user");  //读取模型中的数据
            if (user != null) {
                user.setUserName("Jetty");
                sessionStatus.setComplete();  //让SpringMVC清除本处理器对应的会话属性
            }
            return "/user/showUser";
        }

四、处理方法的数据绑定
    在请求消息到达真正调用处理方法的这一段时间内,Spring MVC还完成了很多工作,包括数据转换、数据格式化及数据校验等。
1. 数据绑定流程
    Spring MVC通过反射机制对目标处理方法的签名进行分析,将请求消息绑定到处理方法的入参中。
(1)Spring MVC主框架将ServletRequest对象及处理方法的入参对象实例传递给DataBinder
(2)DataBinder调用装配在Spring Web上下文中的ConversionService组件进行数据类型转换、数据格式化的工作,将ServletRequest中的消息填充到入参对象中。
(3)DataBinder对象继续调用Validator组件对已经绑定了请求消息数据的入参对象进行数据合法性校验。最终生成BindingResult对象,将它们赋给处理方法的入参。

2. 数据转换
    Spring3.0在核心模型中添加了一个通用的类型转换模块,位于org.springframework.core.convert包中,同时支持Java标准的PropertyEditer。
(1)ConversionService是Spring类型转换体系的核心接口。
(2)Spring支持的转换器
    Converter<S,T>
    GenericConverter
    ConverterFactory
(3)在Spring MVC中使用ConversionService自定义转换器

3. 数据格式化
    Spring3.0引入了一个新的格式化框架,位于org.springframework.format包中,
(1)Formatter<T>是格式化框架最重要的接口
(2)注解驱动格式化的重要接口
(3)启用注解驱动格式化功能
(4)注解驱动格式化实例

4. 数据校验
(1)JSR303是Java为Bean数据合法性校验所提供的标准框架,它已经包含在Java EE6.0中。通过在Bean属性上标注类似于@NotNull等标准的注解指定校验规则
(2)Spring校验框架,它在org.springframework.validation包下,通过注解驱动的方式进行数据校验。
(3)Spring数据校验(获取校验结果、如何在页面中显示错误、通过国际化资源显示错误信息)
(4)自定义校验规则(为请求处理类装配一个自定义的Validator)

五、视图和视图解析器
    请求处理方法执行完成后,最终返回一个ModelAndView对象,对于那些String、View或ModelMap等类型的处理方法,SpringMVC也会在内部将它们装配成一个ModelAndView对象,它包含了实图逻辑名和模型对象的信息。
1. 视图,是渲染模型数据,将模型里的数据以某种形式呈现给客户(JSP、Excel、PDF等)。
2. 视图解析器,将逻辑视图名解析为一个具体的视图对象。
3. JSP和JSTL
(1)使用InternalResourceViewResolver作为视图解析器(JSP)
(2)使用Spring表单标签,它可以很容易地将模型数据中的表单/命令对象绑定到HTML表单元素中
4. 模板视图
    FreeMarker和Velocity是除了JSP之外被使用最多的页面模板技术。页面模板编写好页面结构,模板页面中使用一些特殊的变量标识符绑定Java对象的动态数据。
5. Excel
    如果希望使用Excel展示用户列表,仅需要扩展Spring的AbstractExcelView或AbstractJExcelView即可。
6. PDF
    PDF视图和Excel类似,也是使用一个Bean作为视图对象
7. XML
    将模型中的数据以XML的形式输出,对应视图对象为MarshallingView
8. JSON
    SpringMVC的MappingJacksonJsonView借助Jackson框架的ObjectMapper将模型数据转换为JSON格式输出。
    默认情况下,MappingJacksonJsonView会将模型中的所有数据都输出为JSON,这显然是不适合的,可以通过renderedAttravutes指定模型中哪些属性需要输出。
9. XmlViewResolver
    同BeanNameViewResolver,但它可以将视图Bean定义在一个独立的XML文件中
10. ResourceBundleViewResolver
    它通过一个国际化资源文件定义视图对象,为不同地区的用户提供不同类型的视图
11. 混合使用多种视图技术
(1)ContentNegotiatingViewResolver
(2)使用同一URL获取不同形式的返回内容

六、本地化解析
1. 本地化概述
    浏览器中设置的本地化类型会包含在HTML请求报文头中发送给Web服务器,确切地说是通过报文头的Accept-Language参数将“语言首选项”对话框中选择的语言发送到服务器,成为服务器判别客户端本地化类型的依据。可以通过IE菜单:工具--Internet选项--语言--选择本地化类型设置。

2. 使用CookieLocaleResolver,DispactcherServlet会自动识别本地化解析器并装配它。
3. 使用SessionLocaleResolver,查找Session中属性名为SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME的属性,并将其转换为Locale对象。
4. 使用LocaleChangeInterceptor,通过一个请求参数控制网站的本地化。

七、文件上传
    SpringMVC为文件上传提供了即插即用的MultipartResolver,采用了Jakarta Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。
1. 配置MultipartResolver
Java代码  收藏代码
  1. <!-- 文件上传 -->  
  2. <bean id="multipartResolver"  
  3.   class="org.springframework.web.multipart.commons.CommonsMultipartResolver"  
  4.          p:defaultEncoding="UTF-8"  
  5.          p:maxUploadSize="5000000"  
  6.          p:uploadTempDir="upload/temp"/>    


2. 编写控制器
Java代码  收藏代码
  1. @RequestMapping(value = "/upload")   
  2. public String updateThumb(@RequestParam("name") String name,  
  3.                     @RequestParam("file") MultipartFile file) throws Exception{  
  4.  if (!file.isEmpty()) {  
  5.   file.transferTo(new File("d:/temp/"+file.getOriginalFilename()));  
  6.   return "redirect:success.html";  
  7.  }else{  
  8.   return "redirect:fail.html";  
  9.  }  
  10. }  


SpringMVC会将上传文件绑定到MultipartFile对象中。使用如下:
    byte[] getBytes():获取文件数据
    String getContentType():获取文件MIME类型
    InputStream getInputStream():获取文件流
    String getName():获取表单中文件组件的名字
    String getOriginalFilename():获取上传文件的原名
    long getSize():获取文件的字节大小,单位byte
    boolean isEmpty():是否有上传的文件
    void transferTo(File dest):将上传文件保存到一个目标文件中

3. 表单
    负责上传文件的表单编码必须是“multipart/form-data”类型。
Java代码  收藏代码
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
  3. <html>  
  4.  <head>  
  5.   <title>请上传用户头像</title>  
  6.  </head>  
  7.  <body>  
  8.   <h1>  
  9.    请选择上传的头像文件  
  10.   </h1>  
  11.   <form method="post" action="<c:url value="/user/upload.html"/>" enctype="multipart/form-data">  
  12.    <input type="text" name="name" />  
  13.    <input type="file" name="file" />  
  14.    <input type="submit" />  
  15.   </form>  
  16.  </body>  
  17. </html>  


八、其它
1. 静态资源处理

(1)将DispatcherServlet请求映射配置为"/",使其捕获Web容器所有的请求。
Java代码  收藏代码
  1. <filter>  
  2.     <filter-name>Set Character Encoding</filter-name>  
  3.     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  4.     <init-param>  
  5.         <param-name>encoding</param-name>  
  6.         <param-value>UTF-8</param-value>  
  7.     </init-param>  
  8. </filter>  
  9. <filter-mapping>  
  10.     <filter-name>Set Character Encoding</filter-name>  
  11.     <url-pattern>/*</url-pattern>  
  12. </filter-mapping>  


(2)Spring MVC配置
    <!-- 如果是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,-->
    <mvc:default-servlet-handler/>
   
    <!-- 由Spring MVC框架自己处理静态资源,将Web根路径“/”及类路径下/META-INF/publicResources/的目录映射为/resources路径。假设Web根路径下拥有js/ext.js,则在jsp则可通过 "/resources/js/ext.js访问" -->
    <mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/publicResources/"/>  
    <mvc:resources/>允许静态资源放在任何地方,如WEB-INF目录下、类路径下等。在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,则直接返回303响应状态码,指示客户端使用浏览器缓存的数据,而非将静态资源的内容输出到客户端。如果在映射的物理路径下存在多个同名静态资源,找到第1个就返回,查找的顺序和物理路径在location中的配置顺序一致。
    <mvc:resources mapping="/resources/**"
location="/" cache-period="31536000"/>   
    通过cache-period属性设置静态资源在客户端浏览器中的缓存有效时间,一般设置为一年。

    有时由于浏览器本身缓存管理机制的问题,当我们发布新的版本时客户端并不会从服务器端下载新的静态资源。一个好的解决办法是:网页中引用静态资源的路径添加应用的发布版本号,这样由于版本号的变更造成网页中静态资源路径发生更改,从而使这些静态资源成为“新的资源”。下面将版本号包含到<mvc:resources/>中。

创建版本号获取类:
Java代码  收藏代码
  1. import javax.servlet.ServletContext;  
  2. import org.springframework.web.context.ServletContextAware;  
  3.   
  4. public class ResourcePathExposer implements ServletContextAware {  
  5.  private ServletContext servletContext;  
  6.  private String resourceRoot;  
  7.   
  8.  public void init() {  
  9.   String version = "1.2.1";  
  10.   resourceRoot = "/resources-" + version;  
  11.   getServletContext().setAttribute("resourceRoot",   
  12.     getServletContext().getContextPath()+resourceRoot);  
  13.  }  
  14.   
  15.  public void setServletContext(ServletContext servletContext) {  
  16.   this.servletContext = servletContext;  
  17.  }  
  18.   
  19.  public String getResourceRoot() {  
  20.   return resourceRoot;  
  21.  }  
  22.   
  23.  public ServletContext getServletContext() {  
  24.   return servletContext;  
  25.  }    
  26. }  


在<mvc:resources/>中配置:
Java代码  收藏代码
  1. <bean id="rpe" class="com.baobaotao.web.ResourcePathExposer"  
  2. t-method="init"/>    
  3. <mvc:resources mapping="#{rpe.resourceRoot}/**"   
  4. tion="/" cache-period="31536000"/>    


在jsp中引用路径:
Java代码  收藏代码
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
  3. <html>  
  4.   <head>  
  5.     <title>静态资源测试</title>  
  6.     <script src="${resourceRoot}/js/test.js" type="text/javascript"> </script>  
  7.   </head>   
  8.   <body>  
  9.     hello!!  
  10.     <script>  
  11.        test();   
  12.     </script>  
  13.   </body>  
  14. </html>  


2. 异常处理
    SpringMVC通过HandlerExceptionResolver处理程序的异常,包括处理器映射、数据绑定以及处理器执行时发生异常。
4 0