SpringMvc笔记二

SpringMVC概要:

  1.    Spring为展现层提供的基于MVC设计理念的优秀WEB框架,是目前最主流的MVC框架之一
  2.   Spring3.0之后全面超越Struts2.0成为最优秀的MVC框架
  3.   Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,而无需实现任何借口
  4.   支持REST风格的URL请求
  5.   采用啦松散耦合可插拔组件结构,比其他MVC框架更具有拓展性和灵活性

SpringMVC的HelloWorld

  步骤:

    1.加入jar包:

                

     2.在web.xml中配置DispatcherServlet

    

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <!-- 
    配置 org.springframework.web.filter.HiddenHttpMethodFilter: 可以把 POST 请求转为 DELETE 或 POST 请求 
    -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置 DispatcherServlet -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
        <!-- 
            实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.
            默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
        -->
        <!--  
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
View Code

    3.加入SpringMVC的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">                    

    <!-- scan the package and the sub package -->
    <context:component-scan base-package="test.SpringMVC"/>

    <!-- don't handle the static resource -->
    <mvc:default-servlet-handler />

    <!-- if you use annotation you must configure following setting -->
    <mvc:annotation-driven />
    
    <!-- configure the InternalResourceViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
            id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>
View Code

    4.编写处理请求的处理器,并标识为处理器(Controller)

package com.atguigu.springmvc.handlers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorld {

    /**
     * 1. 使用 @RequestMapping 注解来映射请求的 URL
     * 2. 返回值会通过视图解析器解析为实际的物理视图, 对于 InternalResourceViewResolver 视图解析器, 会做如下的解析:
     * 通过 prefix + returnVal + 后缀 这样的方式得到实际的物理视图, 然会做转发操作
     * 
     * /WEB-INF/views/success.jsp
     * 
     * @return
     */
    @RequestMapping("/helloworld")
    public String hello(){
        System.out.println("hello world");
        return "success";
    }
    
}
View Code

    5.编写视图

xxx.jsp

         6.完成;

详情解析:

使用@RequestMapping映射请求

  @RequestMapping:

     
       1. @RequestMapping 除了修饰方法, 还可来修饰 2. 1). 类定义处: 提供初步的请求映射信息。相对于 WEB 应用的根目录
       2. 方法处: 提供进一步的细分映射信息。 相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法处标记的 URL相对于 WEB 应用的根目录
    

Spring MVC 使用 @RequestMapping 注解为控制器指定可• 以处理哪些 URL 请求 在控制器的类定义及方法定义处都可标注 •

  @RequestMapping 类定义处:提供初步的请求映射信息。相对于  WEB 应用的根目录–

  方法处:提供进一步的细分映射信息。相对于类定义处的 URL。若– 类定义处未标注 @RequestMapping,则方法处标记的 URL 相对于 WEB 应用的根目录 DispatcherServlet 截获请求后,

    就通过控制器上 • @RequestMapping 提供的映射信息确定请求所对应的处理 方法。

    /**
     * 1. @RequestMapping 除了修饰方法, 还可来修饰类 2. 1). 类定义处: 提供初步的请求映射信息。相对于 WEB 应用的根目录
     * 2). 方法处: 提供进一步的细分映射信息。 相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法处标记的 URL
     * 相对于 WEB 应用的根目录
     */
    @RequestMapping("/testRequestMapping")
    public String testRequestMapping() {
        System.out.println("testRequestMapping");
        return SUCCESS;

映射请求参数&请求头

@RequestMapping 除了可以使用请求 URL 映射请求外,

• 还可以使用请求方法、请求参数及请求头映射请求

@RequestMapping 的 value、method、params 及 heads •

分别表示请求 URL、请求方法、请求参数及请求头的映射条 件,他们之间是与的关系,联合使用多个条件可让请求映射 更加精确化。

params 和 headers支持简单的表达式:

  -- param1: 表示请求必须包含名为 param1 的请求参数

  –!param1: 表示请求不能包含名为 param1 的请求参数

  – param1 != value1: 表示请求包含名为 param1 的请求参数,但其值– 不能为 value1

  -- {“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 – 的两个请求参数,且 param1 参数的值必须为 value1

  

/**
     * 了解: 可以使用 params 和 headers 来更加精确的映射请求. params 和 headers 支持简单的表达式.
     * 
     * @return
     */
    @RequestMapping(value = "testParamsAndHeaders", params = { "username",
            "age!=10" }, headers = { "Accept-Language=en-US,zh;q=0.8" })
    public String testParamsAndHeaders() {
        System.out.println("testParamsAndHeaders");
        return SUCCESS;
    }
/**
     * 了解: 映射请求头信息 用法同 @RequestParam
     */
    @RequestMapping("/testRequestHeader")
    public String testRequestHeader(
            @RequestHeader(value = "Accept-Language") String al) {
        System.out.println("testRequestHeader, Accept-Language: " + al);
        return SUCCESS;
    }

通配符:

Ant 风格资源地址支持 3 种匹配符:
• ?:匹配文件名中的一个字符– *:匹配文件名中的任意字符– **:
** 匹配多层路径– @RequestMapping 还支持 Ant 风格的 URL:
• /user/*/createUser: 匹配 – /user/aaa/createUser、/user/bbb/createUser 等
URL /user/*
*/createUser: 匹配
– /user/createUser、/user/aaa/bbb/createUser 等 URL /user/createUser??: 匹配
– /user/createUseraa、/user/createUserbb 等 URL

Rest 风格:
  REST:即 Representational State Transfer。
  (资源)表现层状态转化。是目前• 最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、
   扩展方便, 所以正得到越来越多网站的采用

资源(Resources):
    网络上的一个实体,或者说是网络上的一个具体信息。它• 可以是一段文本、一张图片、一首歌曲、一种服务,
    总之就是一个具体的存在。 可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要 获取这个资源,访问它的URI就可以
    ,因此 URI 即为每一个资源的独一无二的识 别符。

表现层(Representation):
  把资源具体呈现出来的形式,叫做它的表现层• (Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格 式、
  XML 格式、JSON 格式表现,甚至可以采用二进制格式。 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一
  次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器 端。因此,如果客户端想要操作服务器,必须通过某种手段,
  让服务器端发生“ 状态转化”(State Transfer)。
  而这种转化是建立在表现层之上的,所以就是 “ 表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE
  它们分别对应四种基本操作:GET 用来获 取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

示例:
• /order/1  HTTP GET :得到 id = 1 的 order  
– /order/1  HTTP DELETE:删除 id = 1的 order  
– /order/1  HTTP PUT:更新id = 1的 order  
– /order     HTTP POST:新增 order
– HiddenHttpMethodFilter:
浏览器 form 表单只支持 GET • 与 POST 请求,而DELETE、PUT 等 method 并不支 持,
Spring3.0 添加了一个过滤器,可以将这些请求转换 为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求
    <!--
    配置 org.springframework.web.filter.HiddenHttpMethodFilter: 可以把 POST 请求转为 DELETE 或 PUT 请求
    -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
配置web.xml


@PathVariable 绑定 URL 占位符到入参
带占位符的 URL 是 Spring3.0 新增的功能,该功能在 • SpringMVC 向 REST 目标挺进发展过程中具有里程碑的 意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控• 制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过 @PathVariable("xxx") 绑定到操作方法的入参中。

/**
     * Rest 风格的 URL. 以 CRUD 为例: 新增: /order POST 修改: /order/1 PUT update?id=1 获取:
     * /order/1 GET get?id=1 删除: /order/1 DELETE delete?id=1
     * 
     * 如何发送 PUT 请求和 DELETE 请求呢 ? 1. 需要配置 HiddenHttpMethodFilter 2. 需要发送 POST 请求
     * 3. 需要在发送 POST 请求时携带一个 name="_method" 的隐藏域, 值为 DELETE 或 PUT
     * 
     * 在 SpringMVC 的目标方法中如何得到 id 呢? 使用 @PathVariable 注解
     * 
     */
    @RequestMapping(value = "/testRest/{id}", method = RequestMethod.PUT)
    public String testRestPut(@PathVariable Integer id) {
        System.out.println("testRest Put: " + id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testRest/{id}", method = RequestMethod.DELETE)
    public String testRestDelete(@PathVariable Integer id) {
        System.out.println("testRest Delete: " + id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testRest", method = RequestMethod.POST)
    public String testRest() {
        System.out.println("testRest POST");
        return SUCCESS;
    }

    @RequestMapping(value = "/testRest/{id}", method = RequestMethod.GET)
    public String testRest(@PathVariable Integer id) {
        System.out.println("testRest GET: " + id);
        return SUCCESS;
    }

 请求处理方法签名:

  Spring MVC 通过分析处理方法的签名,将 HTTP 请求信• 息绑定到处理方法的相应人参中。 Spring MVC 对控制器处理方法签名的限制是很宽松的,

  几乎可以按喜欢的任何方式对方法进行签名。 必要时可以对方法及方法入参标注相应的注解( • @PathVariable 、@RequestParam、@RequestHeader 等)、

  Spring MVC 框架会将 HTTP 请求的信息绑定到相应的方法入参 中,并根据方法的返回值类型做出相应的后续处理。

使用 @RequestParam 绑定请求参数值


在处理方法入参处使用 @RequestParam 可以把请求参• 数传递给请求方法 value:参数名– required:是否必须。默认为 true, 表示请求参数中必须包含对应– 的参数,若不存在,将抛出异常

/**
     * @RequestParam 来映射请求参数. value 值即请求参数的参数名 required 该参数是否必须. 默认为 true
     *               defaultValue 请求参数的默认值
     */
    @RequestMapping(value = "/testRequestParam")
    public String testRequestParam(
            @RequestParam(value = "username") String un,
            @RequestParam(value = "age", required = false, defaultValue = "0") int age) {
        System.out.println("testRequestParam, username: " + un + ", age: "
                + age);
        return SUCCESS;
    }

使用 @RequestHeader 绑定请求报头的属性值:


请求头包含了若干个属性,服务器可据此获知客户端的信• 息,通过 @RequestHeader 即可将请求头中的属性值绑 定到处理方法的入参中

/**
     * 了解: 映射请求头信息 用法同 @RequestParam
     */
    @RequestMapping("/testRequestHeader")
    public String testRequestHeader(
            @RequestHeader(value = "Accept-Language") String al) {
        System.out.println("testRequestHeader, Accept-Language: " + al);
        return SUCCESS;
    }

使用 @CookieValue 绑定请求中的 Cookie 值:


@CookieValue 可让处理方法入参绑定某个 Cookie值

/**
     * 了解:
     * 
     * @CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
     */
    @RequestMapping("/testCookieValue")
    public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
        System.out.println("testCookieValue: sessionId: " + sessionId);
        return SUCCESS;
    }

使用POJO对象绑定请求参数值

POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans

使用 POJO 对象绑定请求参数值
Spring MVC 会按请求参数名和 POJO 属性名进行自动匹• 配,自动为该对象填充属性值。支持级联属性。 如:dept.deptId、dept.address.tel 等

使用ServletAPI作为入参:

MVC 的 Handler 方法可以接受 哪些 ServletAPI 类型的参数

HttpServletRequest
• HttpServletResponse
• HttpSession• java.security.Principal
• Locale
• InputStream
• OutputStream
• Reader
• Writer
@RequestMapping("/testServletAPI")
    public void testServletAPI(HttpServletRequest request,
                               HttpServletResponse response, Writer out) throws IOException {
        System.out.println("testServletAPI, " + request + ", " + response);
        out.write("hello springmvc");
    }

处理模型数据

   ModelAndView: 处理方法返回值类型为 ModelAndView – 时, 方法体即可通过该对象添加模型数据

  Map 及 Model: 入参为

  – org.springframework.ui.Model、org.springframework.ui. ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。

  @SessionAttributes: 将模型中的某个属性暂存到  HttpSession 中,以便多个请求之间可以共享这个属性

  @ModelAttribute: 方法入参标注该注解后, 入参的对象– 就会放到数据模型中

ModelAndView

控制器处理方法的返回值如果为 ModelAndView, 则其既• 包含视图信息,也包含模型数据信息。 添加模型数据:
• MoelAndView addObject(String attributeName, Object – attributeValue) 
• ModelAndView addAllObject(Map<String, ?> modelMap)
– 设置视图:
• void setView(View view)
• void setViewName(String viewName)j
/**
     * 目标方法的返回值可以是 ModelAndView 类型。 
     * 其中可以包含视图和模型信息
     * SpringMVC 会把 ModelAndView 的 model 中数据放入到 request 域对象中. 
     * @return
     */
    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView(){
        String viewName = SUCCESS;
        ModelAndView modelAndView = new ModelAndView(viewName);
        
        //添加模型数据到 ModelAndView 中.
        modelAndView.addObject("time", new Date());
        
        return modelAndView;
    }

Map及Model

Spring MVC 在内部使用了一个 • org.springframework.ui.Model 接口存 储模型数据

具体步骤

• Spring MVC 在调用方法前会创建一个隐– 含的模型对象作为模型数据的存储容器。

• 如果方法的入参为 Map 或 Model 类– 型,Spring MVC 会将隐含模型的引用传 递给这些入参。

在方法体内,开发者可以 通过这个入参对象访问到模型中的所有数 据,也可以向模型中添加新的属性数据

/**
     * 目标方法可以添加 Map 类型(实际上也可以是 Model 类型或 ModelMap 类型)的参数. 
     * @param map
     * @return
     */
    @RequestMapping("/testMap")
    public String testMap(Map<String, Object> map){
        System.out.println(map.getClass().getName()); 
        map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
        return SUCCESS;
    }
View Code

@SessionAttributes

若希望在多个请求之间共用某个模型属性数据,则可以在• 控制器类上标注一个 @SessionAttributes, Spring MVC 将在模型中对应的属性暂存到 HttpSession 中。

@SessionAttributes 除了可以通过属性名指定需要放到会• 话中的属性外,还可以通过模型属性的对象类型指定哪些 模型属性需要放到会话中

@SessionAttributes(types=User.class) 会将隐含模型中所有类型– 为 User.class 的属性添加到会话中。

@SessionAttributes(value={“user1”, “user2”})

– @SessionAttributes(types={User.class, Dept.class})

– @SessionAttributes(value={“user1”, “user2”},

– types={Dept.class})

    /**
     * @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外(实际上使用的是 value 属性值),
     * 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(实际上使用的是 types 属性值)
     * 
     * 注意: 该注解只能放在类的上面. 而不能修饰放方法. 
     */
    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(Map<String, Object> map){
        User user = new User("Tom", "123456", "tom@atguigu.com", 15);
        map.put("user", user);
        map.put("school", "atguigu");
        return SUCCESS;
View Code

@ModelAttribute

在方法定义上使用 @ModelAttribute 注解:Spring MVC • 在调用目标处理方法前,会先逐个调用在方法级上标注了 @ModelAttribute 的方法。 在方法的入参前使用 @ModelAttribute 注解:• 可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数– 绑定到对象中,再传入入参 将方法入参对象添加到模型中

/**
     * 1. 有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用! 
     * 2. @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
     * 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
     * 2). SpringMVC 会一 value 为 key, POJO 类型的对象为 value, 存入到 request 中. 
     */
    @ModelAttribute
    public void getUser(@RequestParam(value="id",required=false) Integer id, 
            Map<String, Object> map){
        System.out.println("modelAttribute method");
        if(id != null){
            //模拟从数据库中获取对象
            User user = new User(1, "Tom", "123456", "tom@atguigu.com", 12);
            System.out.println("从数据库中获取一个对象: " + user);
            
            map.put("user", user);
        }
    }
View Code
/**
     * 运行流程:
     * 1. 执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象, 把对象放入到了 Map 中. 键为: user
     * 2. SpringMVC 从 Map 中取出 User 对象, 并把表单的请求参数赋给该 User 对象的对应属性.
     * 3. SpringMVC 把上述对象传入目标方法的参数. 
     * 
     * 注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
     * 
     * SpringMVC 确定目标方法 POJO 类型入参的过程
     * 1. 确定一个 key:
     * 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
     * 2). 若使用了  @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值. 
     * 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
     * 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到. 
     * 3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰, 
     * 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
     * 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常. 
     * 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
     * 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
     * 5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中. 
     * 
     * 源代码分析的流程
     * 1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
     * 2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
     * 1). 创建 WebDataBinder 对象:
     * ①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写. 
     * *注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute 
     * 的 value 属性值 
     * 
     * ②. 确定 target 属性:
     *     > 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
     *     > *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中
     * 获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常. 
     *     > 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key
     * 和 attrName 相匹配, 则通过反射创建了 POJO 对象
     * 
     * 2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性. 
     * 3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel. 
     * 近而传到 request 域对象中. 
     * 4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参. 
     */
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(User user){
        System.out.println("修改: " + user);
        return SUCCESS;
    }
View Code

 SessionAttribute引发异常

如何避免:

视图和视图解析器

视图和视图解析器定义:

请求处理方法执行完成后,最终返回一个 ModelAndView • 对象。对于那些返回 String,View 或 ModeMap 等类型的 处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图

Spring MVC 借助视图解析器(ViewResolver)得到最终• 的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart  等各种表现形式的视图

对于最终究竟采取何种视图对象对模型数据进行渲染,处• 理器并不关心,处理器工作重点聚焦在生产模型数据的工 作上,从而实现 MVC 的充分解耦

视图:

视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客• 户。 为了实现视图模型和具体实现技术的解耦,Spring 在 • org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口:


视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们• 不会有线程安全的问题

 视图解析器:

SpringMVC 为逻辑视图名的解析提供了不同的策略,可•以在 Spring WEB 上下文中配置一种或多种解析策略,并 指定他们之间的先后顺序。每一种映射策略对应一个具体 的视图解析器实现类。

视图解析器的作用比较单一:将逻辑视图解析为一个具体• 的视图对象。

所有的视图解析器都必须实现 ViewResolver 接口

 

 

 • 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可 以通过 order 属性指定解析器的优先顺序,order  越小优先级越高。 SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解• 析,直到解析成功并返回视图对象,否则将抛出 ServletException 异 常

 InternalResourceViewResolver

JSP 是最常见的视图技术,可以使用 • InternalResourceViewResolver 作为视图解析器:

 

若项目中使用了 JSTL,则 SpringMVC 会自动把视图由 InternalResourceView 转为 JstlView

若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际• 化资源文件

若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-• controller 标签实现

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 配置自定扫描的包 -->
    <context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>

    <!-- 配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    
    <!-- 配置视图  BeanNameViewResolver 解析器: 使用视图的名字来解析视图 -->
    <!-- 通过 order 属性来定义视图解析器的优先级, order 值越小优先级越高 -->
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <property name="order" value="100"></property>
    </bean>
    
    <!-- 配置国际化资源文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"></property>    
    </bean>
    
    <!-- 配置直接转发的页面 -->
    <!-- 可以直接相应转发的页面, 而无需再经过 Handler 的方法.  -->
    <mvc:view-controller path="/success" view-name="success"/>
    
    <!-- 在实际开发中通常都需配置 mvc:annotation-driven 标签 -->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>
View Code

Excel视图:

      若希望使用 Excel 展示数据列表,仅需要扩展 • SpringMVC 提供的                AbstractExcelViewAbstractJExcel View 即可。实现 buildExcelDocument() 方法,      在方法中使用模型数据对象构建 Excel 文档就可以 了。 AbstractExcelView 基于 POI       API,而 AbstractJExcelView 是基于 JExcelAPI 的。 视图对象需要配置 IOC 容器中      的一个 Bean,使用 • BeanNameViewResolver 作为视图解析器即可 若希望直接在浏      览器中直接下载 Excel 文档,则可以设置• 响应头 Content-Disposition 的值为         attachment;filename=xxx.xls

关于重定向:

一般情况下,控制器方法返回字符串类型的值会被当成逻• 辑视图名处理 如果返回的字符串中带 forward: 或 redirect: 前缀• 时,SpringMVC 会对他们进行特殊处理:将 forward: redirect: 当成指示符,其后的字符串作为 URL 来处理

redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作

forward:success.jsp:会完成一个到 success.jsp 的转发操作

RESTful CRUD

1. 显示所有员工信息

• URI:emps

– 请求方式:

GET– 显示效果

2. 添加所有员工信息

• 显示添加页面:

– URI:emp

• 请求方式:GET• 显示效果

•添加员工信息:– URI:emp

• 请求方式:POST• 显示效果:完成添加,重定向到 list 页•面。

 3. 删除操作

• URL:emp/{id}

– 请求方式:DELETE

– 删除后效果:对应记录从数据表中删除

– 4. 修改操作:

lastName 不可修改 !• 显示修改页面:

– URI:emp/{id}• 请求方式:GET• 显示效果:回显表单。• 修改员工信息:

– URI:emp • 请求方式:PUT• 显示效果:完成修改,重定向到 list 页面

 

相关的类:

• 实体类:Employee、Department– Handler:EmployeeHandler– Dao:EmployeeDao、DepartmentDao

– 相关的页面• list.jsp– input.jsp– edit.jsp

entities:

package com.atguigu.springmvc.crud.entities;

import java.util.Date;

import javax.validation.constraints.Past;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

public class Employee {

    private Integer id;
    @NotEmpty
    private String lastName;

    @Email
    private String email;
    //1 male, 0 female
    private Integer gender;
    
    private Department department;
    
    @Past
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birth;
    
    @NumberFormat(pattern="#,###,###.#")
    private Float salary;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Float getSalary() {
        return salary;
    }

    public void setSalary(Float salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", lastName=" + lastName + ", email="
                + email + ", gender=" + gender + ", department=" + department
                + ", birth=" + birth + ", salary=" + salary + "]";
    }

    public Employee(Integer id, String lastName, String email, Integer gender,
            Department department) {
        super();
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
        this.department = department;
    }

    public Employee() {
        // TODO Auto-generated constructor stub
    }
}
Employee.java
package com.atguigu.springmvc.crud.entities;

public class Department {

    private Integer id;
    private String departmentName;

    public Department() {
        // TODO Auto-generated constructor stub
    }
    
    public Department(int i, String string) {
        this.id = i;
        this.departmentName = string;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }

    @Override
    public String toString() {
        return "Department [id=" + id + ", departmentName=" + departmentName
                + "]";
    }
    
}
Department.java

dao:

package com.atguigu.springmvc.crud.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.atguigu.springmvc.crud.entities.Department;
import com.atguigu.springmvc.crud.entities.Employee;

@Repository
public class EmployeeDao {

    private static Map<Integer, Employee> employees = null;
    
    @Autowired
    private DepartmentDao departmentDao;
    
    static{
        employees = new HashMap<Integer, Employee>();

        employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
        employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
        employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
        employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
        employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
    }
    
    private static Integer initId = 1006;
    
    public void save(Employee employee){
        if(employee.getId() == null){
            employee.setId(initId++);
        }
        
        employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
    }
    
    public Collection<Employee> getAll(){
        return employees.values();
    }
    
    public Employee get(Integer id){
        return employees.get(id);
    }
    
    public void delete(Integer id){
        employees.remove(id);
    }
}
EmployeeDao.java
package com.atguigu.springmvc.crud.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.atguigu.springmvc.crud.entities.Department;

@Repository
public class DepartmentDao {

    private static Map<Integer, Department> departments = null;
    
    static{
        departments = new HashMap<Integer, Department>();
        
        departments.put(101, new Department(101, "D-AA"));
        departments.put(102, new Department(102, "D-BB"));
        departments.put(103, new Department(103, "D-CC"));
        departments.put(104, new Department(104, "D-DD"));
        departments.put(105, new Department(105, "D-EE"));
    }
    
    public Collection<Department> getDepartments(){
        return departments.values();
    }
    
    public Department getDepartment(Integer id){
        return departments.get(id);
    }
    
}
DepartmentDao.java

handler:

package com.atguigu.springmvc.crud.handlers;

import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.atguigu.springmvc.crud.dao.DepartmentDao;
import com.atguigu.springmvc.crud.dao.EmployeeDao;
import com.atguigu.springmvc.crud.entities.Employee;

@Controller
public class EmployeeHandler {

    @Autowired
    private EmployeeDao employeeDao;
    
    @Autowired
    private DepartmentDao departmentDao;

    @ModelAttribute
    public void getEmployee(@RequestParam(value="id",required=false) Integer id,
            Map<String, Object> map){
        if(id != null){
            map.put("employee", employeeDao.get(id));
        }
    }
    
    @RequestMapping(value="/emp", method=RequestMethod.PUT)
    public String update(Employee employee){
        employeeDao.save(employee);
        
        return "redirect:/emps";
    }
    
    @RequestMapping(value="/emp/{id}", method=RequestMethod.GET)
    public String input(@PathVariable("id") Integer id, Map<String, Object> map){
        map.put("employee", employeeDao.get(id));
        map.put("departments", departmentDao.getDepartments());
        return "input";
    }
    
    @RequestMapping(value="/emp/{id}", method=RequestMethod.DELETE)
    public String delete(@PathVariable("id") Integer id){
        employeeDao.delete(id);
        return "redirect:/emps";
    }
    
    @RequestMapping(value="/emp", method=RequestMethod.POST)
    public String save(@Valid Employee employee, Errors result, 
            Map<String, Object> map){
        System.out.println("save: " + employee);
        
        if(result.getErrorCount() > 0){
            System.out.println("出错了!");
            
            for(FieldError error:result.getFieldErrors()){
                System.out.println(error.getField() + ":" + error.getDefaultMessage());
            }
            
            //若验证出错, 则转向定制的页面
            map.put("departments", departmentDao.getDepartments());
            return "input";
        }
        
        employeeDao.save(employee);
        return "redirect:/emps";
    }    
    
    @RequestMapping(value="/emp", method=RequestMethod.GET)
    public String input(Map<String, Object> map){
        map.put("departments", departmentDao.getDepartments());
        map.put("employee", new Employee());
        return "input";
    }
    
    @RequestMapping("/emps")
    public String list(Map<String, Object> map){
        map.put("employees", employeeDao.getAll());
        return "list";
    }
    
//    @InitBinder
//    public void initBinder(WebDataBinder binder){
//        binder.setDisallowedFields("lastName");
//    }
    
}
EmployeeHandler.java

view:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    
    <h4>Success Page</h4>
    
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<!--  
    SpringMVC 处理静态资源:
    1. 为什么会有这样的问题:
    优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀
    若将 DispatcherServlet 请求映射配置为 /, 
    则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理, 
    因找不到对应处理器将导致错误。
    2. 解决: 在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>
-->
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
    $(function(){
        $(".delete").click(function(){
            var href = $(this).attr("href");
            $("form").attr("action", href).submit();            
            return false;
        });
    })
</script>
</head>
<body>
    
    <form action="" method="POST">
        <input type="hidden" name="_method" value="DELETE"/>
    </form>
    
    <c:if test="${empty requestScope.employees }">
        没有任何员工信息.
    </c:if>
    <c:if test="${!empty requestScope.employees }">
        <table border="1" cellpadding="10" cellspacing="0">
            <tr>
                <th>ID</th>
                <th>LastName</th>
                <th>Email</th>
                <th>Gender</th>
                <th>Department</th>
                <th>Edit</th>
                <th>Delete</th>
            </tr>
            
            <c:forEach items="${requestScope.employees }" var="emp">
                <tr>
                    <td>${emp.id }</td>
                    <td>${emp.lastName }</td>
                    <td>${emp.email }</td>
                    <td>${emp.gender == 0 ? 'Female' : 'Male' }</td>
                    <td>${emp.department.departmentName }</td>
                    <td><a href="emp/${emp.id}">Edit</a></td>
                    <td><a class="delete" href="emp/${emp.id}">Delete</a></td>
                </tr>
            </c:forEach>
        </table>
    </c:if>
    
    <br><br>
    
    <a href="emp">Add New Employee</a>
    
</body>
</html>
list.jsp
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

    <form action="testConversionServiceConverer" method="POST">
        <!-- lastname-email-gender-department.id 例如: GG-gg@atguigu.com-0-105 -->
        Employee: <input type="text" name="employee"/>
        <input type="submit" value="Submit"/>
    </form>
    <br><br>
    
    <!--  
        1. WHY 使用 form 标签呢 ?
        可以更快速的开发出表单页面, 而且可以更方便的进行表单值的回显
        2. 注意:
        可以通过 modelAttribute 属性指定绑定的模型属性,
        若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean
        如果该属性值也不存在,则会发生错误。
    -->
    <br><br>
    <form:form action="${pageContext.request.contextPath }/emp" method="POST" 
        modelAttribute="employee">
        
        <form:errors path="*"></form:errors>
        <br>
        
        <c:if test="${employee.id == null }">
            <!-- path 属性对应 html 表单标签的 name 属性值 -->
            LastName: <form:input path="lastName"/>
            <form:errors path="lastName"></form:errors>
        </c:if>
        <c:if test="${employee.id != null }">
            <form:hidden path="id"/>
            <input type="hidden" name="_method" value="PUT"/>
            <%-- 对于 _method 不能使用 form:hidden 标签, 因为 modelAttribute 对应的 bean 中没有 _method 这个属性 --%>
            <%-- 
            <form:hidden path="_method" value="PUT"/>
            --%>
        </c:if>
        
        <br>
        Email: <form:input path="email"/>
        <form:errors path="email"></form:errors>
        <br>
        <% 
            Map<String, String> genders = new HashMap();
            genders.put("1", "Male");
            genders.put("0", "Female");
            
            request.setAttribute("genders", genders);
        %>
        Gender: 
        <br>
        <form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/>
        <br>
        Department: <form:select path="department.id" 
            items="${departments }" itemLabel="departmentName" itemValue="id"></form:select>
        <br>
        <!--  
            1. 数据类型转换
            2. 数据类型格式化
            3. 数据校验. 
            1). 如何校验 ? 注解 ?
            ①. 使用 JSR 303 验证标准
            ②. 加入 hibernate validator 验证框架的 jar 包
            ③. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />
            ④. 需要在 bean 的属性上添加对应的注解
            ⑤. 在目标方法 bean 类型的前面添加 @Valid 注解
            2). 验证出错转向到哪一个页面 ?
            注意: 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
            3). 错误消息 ? 如何显示, 如何把错误消息进行国际化
        -->
        Birth: <form:input path="birth"/>
        <form:errors path="birth"></form:errors>
        <br>
        Salary: <form:input path="salary"/>
        <br>
        <input type="submit" value="Submit"/>
    </form:form>
    
</body>
</html>
input.jsp

配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <!-- 配置 SpringMVC 的 DispatcherServlet -->
    <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <!-- 配置 HiddenHttpMethodFilter: 把 POST 请求转为 DELETE、PUT 请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    
    <!--  
        default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
        它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 
        Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理

        一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
        若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
        
    -->
    <mvc:default-servlet-handler/>

    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>    
    
    <!-- 配置 ConversionService -->
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="employeeConverter"/>
            </set>
        </property>    
    </bean>
    
    <!-- 配置国际化资源文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"></property>
    </bean>
    
    <!-- 配置 SessionLocalResolver -->
    <bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
    
    <mvc:interceptors>
        <!-- 配置自定义的拦截器 -->
        <bean class="com.atguigu.springmvc.interceptors.FirstInterceptor"></bean>
        
        <!-- 配置拦截器(不)作用的路径 -->
        <mvc:interceptor>
            <mvc:mapping path="/emps"/>
            <bean class="com.atguigu.springmvc.interceptors.SecondInterceptor"></bean>
        </mvc:interceptor>
        
        <!-- 配置 LocaleChanceInterceptor -->
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
    </mvc:interceptors>
    
    <!--  
    <mvc:view-controller path="/i18n" view-name="i18n"/>
    -->
    <mvc:view-controller path="/i18n2" view-name="i18n2"/>
    
    <!-- 配置 MultipartResolver -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="1024000"></property>    
    </bean>    
    
    <!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionAttribute" value="ex"></property>
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
            </props>
        </property>
    </bean>    
        
</beans>
springmvc.xml

 jar包:

SpringMVC表单标签

通过 SpringMVC 的表单标签可以实现将模型数据• 中的属性和 HTML 表单元素相绑定,以实现表单 数据更便捷编辑和表单值的回显

form标签:

一般情况下,通过 GET 请求获取表单页面,而通过 • POST 请求提交表单页面,因此获取表单页面和提交表单 页面的 URL 是相同的。

只要满足该最佳条件的契 约,<form:form> 标签就无需通过 action 属性指定表单提交的 URL

可以通过 modelAttribute 属性指定绑定的模型属性,若• 没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean,如果该属性值也不存在,则会 发生错误

SpringMVC 提供了多个表单组件标签,如 • <form:input/>、<form:select/> 等,用以绑定表单字段的 属性值,它们的共有属性如下:

path:表单字段,对应 html 元素的 name 属性,支持级联属性

– htmlEscape:是否对表单值的 HTML 特殊字符进行转换,默认值– 为 true cssClass:表单组件对应的 CSS 样式类名

– cssErrorClass:表单组件的数据存在错误时,采取的 CSS 样式

form:input、form:password、form:hidden、form:textarea• :对应 HTML 表单的 text、password、hidden、textarea 标签

form:radiobutton:单选框组件标签,当表单 bean 对应的• 属性值和 value 值相等时,单选框被选中 form:radiobuttons:单选框组标签,用于构造多个单选• 框 items:可以是一个 List、String[] 或 Map

– itemValue:指定 radio 的 value 值。可以是集合中 bean 的一个– 属性值

--itemLabel:指定 radio 的 label 值

– delimiter:多个单选框可以通过 delimiter 指定分隔符

form:checkbox:复选框组件。用于构造单个复选框

• form:checkboxs:用于构造多个复选框。使用方式同

• form:radiobuttons 标签 form:select:用于构造下拉框组件。使用方式同

• form:radiobuttons 标签 form:option:下拉框选项组件标签。使用方式同

• form:radiobuttons 标签 form:errors:显示表单组件或数据校验所对应的错误

<form:errors path= “ *” /> :显示表单所有的错误

<form:errors path= “ user*” /> :显示所有以 user 为前缀的属性对应的错误

<form:errors path= “ username” /> :显示特定表单对象属性的错误

处理静态资源

优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀• 若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获 • WEB 容器的所有请求,

包括静态资源的请求, SpringMVC 会将他 们当成一个普通请求处理,因找不到对应处理器将导致错误

可以在 SpringMVC 的配置文件中配置

<mvc:default-servlet- handler/>

的方式解决静态资源的问题:

<mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个 – DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的 请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理 一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 – WEB 服务器的默认 Servlet 名称不是 default,则需要通过 defaultservlet-name 属性显式指定

<mvc:annotation-driven></mvc:annotation-driven>
/*
*解决mapping失效
*/

数据转换

数据绑定流程:

1. Spring MVC 主框架将 ServletRequest  对象及目标方 法的入参实例传递给 WebDataBinderFactory 实例,以创 建 DataBinder 实例对象

2. DataBinder 调用装配在 Spring MVC 上下文中的  ConversionService 组件进行数据类型转换、数据格式 化工作。将 Servlet 中的请求信息填充到入参对象中

3. 调用 Validator 组件对已经绑定了请求消息的入参对象• 进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象

4. Spring MVC 抽取 BindingResult 中的入参对象和校验• 错误对象,将它们赋给处理方法的响应入参

Spring MVC 通过反射机制对目标处理方法进行解析,将请• 求消息绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder,运行机制如下:

 

 数据转换:

 

 自定义类型转换器:

ConversionService 是 Spring 类型转换体系的核心接口。
可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService. Spring 将自动识别出 IOC 容器中的 ConversionService,
并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换 可通过 ConversionServiceFactoryBean 的 converters 属性• 注册自定义的类型转换器

 

spring支持的转换器:

Spring 定义了 3 种类型的转换器接口,实现任意一个转换• 器接口都可以作为自定义转换器注册到 ConversionServiceFactroyBean 中:

Converter<S,T>:将 S 类型对象转为 T 类型对象

– ConverterFactory:将相同系列多个 “同质” Converter 封装在一– 起。如果希望将一种类型的对象转换为另一种类型及其子类的对 象(例如将 String 转换为 Number 及 Number 子类 (Integer、Long、Double 等)对象)可使用该转换器工厂类

GenericConverter:会根据源类对象及目标类对象所在的宿主类– 中的上下文信息进行类型转换

 自定义类型转换器示例:

 

 关于 mvc:annotation-driven

<mvc:annotation-driven /> 会自动注 册

RequestMappingHandlerMapping

RequestMappingHandlerAdapter 

ExceptionHandlerExceptionResolver  三个bean。

还将提供以下支持:

– 支持使用 ConversionService 实例对表单参数进行类型转换

– 支持使用 @NumberFormat annotation、@DateTimeFormat 注解完成数据类型的格式化

– 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证

– 支持使用 @RequestBody 和 @ResponseBody 注解

 

@InitBinder

由 @InitBinder 标识的方法,可以对 WebDataBinder 对•象进行初始化。WebDataBinder 是 DataBinder 的子类,用 于完成由表单字段到 JavaBean 属性的绑定
 @InitBinder方法不能有返回值,它必须声明为void。
 @InitBinder方法的参数通常是是 WebDataBinder

数据格式化

1.配置:<mvc:annotation-driven></mvc:annotation-driven>
2.在实体类上标明注解格式:

定义:

对属性对象的输入/输出进行格式化,从其本质上讲依然 属于 “类型转换” 的范畴。 Spring 在格式化模块中定义了一个实现 • ConversionService 接口的 FormattingConversionService 实现类,该实现类扩展 了 GenericConversionService,因此它既具有类型转换的 功能,又具有格式化的功能 FormattingConversionService 拥有一个 • FormattingConversionServiceFactroyBean 工厂类, 后者用于在 Spring 上下文中构造前者

FormattingConversionServiceFactroyBean 内部已经注册了 :

NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性– 使用 @NumberFormat 注解

JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型– 的属性使用 @DateTimeFormat 注解

装配了 FormattingConversionServiceFactroyBean 后,就可• 以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动 了。<mvc:annotation-driven/> 默认创建的 ConversionService 实例即为 FormattingConversionServiceFactroyBean

 日期格式化:

 @DateTimeFormat 注解可对 java.util.Date、java.util.Calendar、java.long.Long 时间 类型进行标注:

pattern 属性:类型为字符串。指定解析/格式化字段数据的模式, 如:”yyyy-MM-dd hh:mm:ss

– iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) -- 默 认、ISO.DATE(yyyy-MM-dd) 、      ISO.TIME(hh:mm:ss.SSSZ)、  ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)

– style 属性:字符串类型。通过样式指定日期时间的格式,由两位字– 符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日 期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整 日期/时间格式、-:忽略日期或时间格式

  数值格式化:

 @NumberFormat 可对类似数字类型的属性进行标 注,它拥有两个互斥的属性:

style:类型为 NumberFormat.Style。用于指定样式类– 型,包括三种:Style.NUMBER(正常数字类型)Style.CURRENCY(货币类型)Style.PERCENT( 百分数类型)

pattern:类型为 String,自定义样式,– 如patter="#,###";

示例:

 

数据校验

JSP303:

JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,• 它已经包含在 JavaEE 6.0 中 . JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max • 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证

 

 Hibernate Validator 扩展注解

Hibernate Validator 是 JSR 303 的一个参考实现,除支持• 所有标准的校验注解外,它还支持以下的扩展注解

 

 Spring MVC 数据校验


Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR • 303 标准的校验框架。

Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式 进行数据校验

Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 • Validator 接口,也实现了 JSR 303 的 Validator 接口。只要 在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校 验的 Bean 中

Spring 本身并没有提供 JSR303 的实现,所以必须将 • JSR303 的实现者的 jar 包放到类路径下。

 2.

<mvc:annotation-driven/> 会默认装配好一个 • LocalValidatorFactoryBean,通过在处理方法的入参上标 注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行 数据校验的工作 在已经标注了 JSR303 注解的表单/命令对象前标注一个 • @Valid,Spring MVC 框架在将请求参数绑定到该入参对象 后,就会调用校验框架根据注解声明的校验规则实施校验 Spring MVC 是通过对处理方法签名的规约来保存校验结果• 的:前一个表单/命令对象的校验结果保存到随后的入参 中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于 org.springframework.validation 包中

需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参:

***Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 • getFieldErrors(String field)

***BindingResult 扩展了 Errors 接口

 在目标方法中获取校验结果:


在表单/命令对象类的属性中标注校验注解,在处理方法对• 应的入参前添加 @Valid,Spring MVC 就会实施校验并将校 验结果保存在被校验入参对象之后的 BindingResult 或 Errors 入参中。 常用方法:

–FieldError getFieldError(String field)

– List<FieldError> getFieldErrors()

– Object getFieldValue(String field)

– Int getErrorCount()

在页面上显示错误


Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult 或 Errors 对象中外,还会将所有校验 结果保存到 “隐含模型” 即使处理方法的签名中没有对应于表单/命令对象的结果• 入参,校验结果也会保存在 “隐含对象” 中。 隐含模型中的所有数据最终将通过 HttpServletRequest 的• 属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取 错误信息 在 JSP 页面上可通过 <form:errors path=“userName”> • 显示错误消息

示例:

 

 提示消息的国际化


每个属性在数据绑定和数据校验发生错误时,都会生成一• 个对应的 FieldError 对象。

当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消 息代码:例如 User 类中的 password 属性标准了一个 @Pattern 注 解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码: –Pattern.user.password

– Pattern.password

– Pattern.java.lang.String

– Pattern

当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 • WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认 的错误消息,否则使用国际化消息。

 若数据类型转换或数据格式转换时发生错误,或该有的参• 数不存在,或调用处理方法时发生错误,都会在隐含模型 中创建错误消息。其错误代码前缀说明如下: required:必要的参数不存在。如 @RequiredParam(“param1”) – 标注了一个入参,但是该参数不存在 typeMismatch:在数据绑定时,发生数据类型不匹配的问题– methodInvocation:Spring MVC 在调用处理方法时发生了错误– 注册国际化资源文件

 

处理JSON:使用HTTPMessageConvert

1.加入架包:

2.编写目标方法,使其返回JSON对应的对象或者集合

3.在方法上添加@ResponseBody注解

 

HttpMessageConverter<T>

HttpMessageConverter<T>

是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象( 类型为 T)输出为响应信息

HttpMessageConverter<T>接口定义的方法

–Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器 可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对 象,同时指定支持   MIME 类型(text/html,applaiction/json等)

– Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器 是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型 在MediaType 中定义。 –LIst<MediaType> getSupportMediaTypes():该转换器支持的媒体类– 型。

–T read(Class<? extends T> clazz,HttpInputMessage inputMessage):– 将请求信息流转换为 T 类型的对象。

–void write(T t,MediaType contnetType,HttpOutputMessgae – outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类 型为 contentType。 

 HttpMessageConverter<T>的实现类

 DispatcherServlet 默认装配 • RequestMappingHandlerAdapter ,而 RequestMappingHandlerAdapter 默认装配如下 HttpMessageConverter:

加入 jackson jar 包后, RequestMappingHandlerAdapter  • 装配的 HttpMessageConverter  如下:

 使用 HttpMessageConverter<T>

将请求信息转化并绑定到处理方法的入• 参中或将响应结果转为对应类型的响应信息,Spring 提供了两种途径

– 使用 @RequestBody / @ResponseBody 对处理方法进行标注

– 使用 HttpEntity<T> / ResponseEntity<T> 作为处理方法的入参或返回值–

当控制器处理方法使用到 @RequestBody/@ResponseBody 或 • HttpEntity<T>/ResponseEntity<T> 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter,  进而根据参数类型或 泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错

@RequestBody 和 @ResponseBody 不需要成对出现

示例:

国际化

国际化概述:
默认情况下,SpringMVC  根据 Accept-Language 参数判断客户端的本地化类型。

当接受到请求时,SpringMVC 会在上下文中查找一个本• 地化解析器(LocalResolver),找到后使用它获取请求 所对应的本地化类型信息。

SpringMVC 还允许装配一个动态更改本地化类型的拦截器,这样通过指定一个请求参数就可以控制单个请求的本 地化类型。

SessionLocaleResolver & LocaleChangeInterceptor 工作原理

本地化解析器和本地化拦截器

AcceptHeaderLocaleResolver:根据 HTTP 请求头的Accept-Language 参数确定本地化类型,如果没有显式定义 本地化解析器, SpringMVC 使用该解析器。 CookieLocaleResolver:根据指定的 Cookie 值确定本地化类型

SessionLocaleResolver:根据 Session 中特定的属性确定本地化类型

LocaleChangeInterceptor:从请求参数中获取本次请求对应• 的本地化类型。

 注意:SessionLocalResolver的id一定要是:localeResolver,不然会找不到抛出异常:Cannot change HTTP accept header - use a different locale resolution strategy

<!--  
        关于国际化:
        1. 在页面上能够根据浏览器语言设置的情况对文本(不是内容), 时间, 数值进行本地化处理
        2. 可以在 bean 中获取国际化资源文件 Locale 对应的消息
        3. 可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
        
        解决:
        1. 使用 JSTL 的 fmt 标签
        2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其对应的 getMessage 方法即可
        3. 配置 LocalResolver 和 LocaleChangeInterceptor
    -->    
View Code

 根据链接切换语言:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    
    <!--  
        default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
        它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 
        Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理

        一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
        若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
        
    -->
    <mvc:default-servlet-handler/>

    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>    
    
    <!-- 配置 ConversionService -->
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="employeeConverter"/>
            </set>
        </property>    
    </bean>
    
    <!-- 配置国际化资源文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"></property>
    </bean>
    
    <!-- 配置 SessionLocalResolver -->
    <bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
    
    <mvc:interceptors>
        <!-- 配置自定义的拦截器 -->
        <bean class="com.atguigu.springmvc.interceptors.FirstInterceptor"></bean>
        
        <!-- 配置拦截器(不)作用的路径 -->
        <mvc:interceptor>
            <mvc:mapping path="/emps"/>
            <bean class="com.atguigu.springmvc.interceptors.SecondInterceptor"></bean>
        </mvc:interceptor>
        
        <!-- 配置 LocaleChanceInterceptor -->
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
    </mvc:interceptors>
    
    <!--  
    <mvc:view-controller path="/i18n" view-name="i18n"/>
    -->
    <mvc:view-controller path="/i18n2" view-name="i18n2"/>
    
    <!-- 配置 MultipartResolver -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="1024000"></property>    
    </bean>    
    
    <!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionAttribute" value="ex"></property>
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
            </props>
        </property>
    </bean>    
        
</beans>
springmvc.xml
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    
    <fmt:message key="i18n.user"></fmt:message>
    
    <br><br>
    <a href="i18n2">I18N2 PAGE</a>
    
    <br><br>
    <a href="i18n?locale=zh_CH">中文</a>
    
    <br><br>
    <a href="i18n?locale=en_US">英文</a>
    
</body>
</html>
i18n.jsp

文件上传

Spring MVC 为文件上传提供了直接的支持,这种支持是通• 过即插即用的 MultipartResolver 实现的。Spring 用 Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResovler

Spring MVC 上下文中默认没有装配 MultipartResovler,因• 此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver

配置MultipartResolver:

defaultEncoding: 必须和用户 JSP 的 pageEncoding 属性• 一致,以便正确解析表单的内容 为了让 CommonsMultipartResovler 正确工作,必须先• 将 Jakarta Commons FileUpload 及 Jakarta Commons io 的类包添加到类路径下

示例:

1.加入jar包:

 

2.配置MultipartResolver:

<!--配置MultipartResolver-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="1024000"></property>
    </bean>
  <form action="testFileUpload" method="post" enctype="multipart/form-data">
      File:<input type="file" name="file">
      DESC:<input type="text" name="desc">
      <input type="submit" value="Submit">
  </form>
@RequestMapping("/testFileUpload")
    public String testFileUpload(@RequestParam("desc") String desc, @RequestParam("file")MultipartFile file) throws IOException {
        System.out.println("desc="+desc);
        System.out.println("FileName="+file.getOriginalFilename());
        System.out.println("InputStream="+file.getInputStream());
        return "success";
    }

使用拦截器

 

自定义拦截器

Spring MVC也可以使用拦截器对请求进行拦截处理,用户• 可以自定义拦截器来实现特定的功能,自定义的拦截器必 须实现HandlerInterceptor接口

---preHandle():这个方法在业务处理器处理请求之前被调用,在该– 方法中对用户请求 request 进行处理。如果程序员决定该拦截器对 请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去 进行处理,则返回true;如果程序员决定不需要再调用其他的组件 去处理请求,则返回false。

---postHandle():这个方法在业务处理器处理完请求后,但– 是DispatcherServlet 向客户端返回响应前被调用,在该方法中对 用户请求request进行处理。

---afterCompletion():这个方法在 DispatcherServlet 完全处理完请– 求后被调用,可以在该方法中进行一些资源清理的操作。

1.编写拦截器:

package com.mikey.www.interceptors;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Mikey
 * @date 2018/10/10 14:48
 */
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("preHandle method now action");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle method now action");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion method now action");
    }
}
FirstInterceptor

2.编写配置文件:springmvc.xml

拦截器执行顺序:

配置自定义拦截器:

 

 注意:如果pre方法返回false后续拦截器不会再执行;

@Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("preHandle method now action");
        return true;//说的就是这里
    }

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class FirstInterceptor implements HandlerInterceptor{

    /**
     * 该方法在目标方法之前被调用.
     * 若返回值为 true, 则继续调用后续的拦截器和目标方法. 
     * 若返回值为 false, 则不会再调用后续的拦截器和目标方法. 
     * 
     * 可以考虑做权限. 日志, 事务等. 
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("[FirstInterceptor] preHandle");
        return true;
    }

    /**
     * 调用目标方法之后, 但渲染视图之前. 
     * 可以对请求域中的属性或视图做出修改. 
     */
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("[FirstInterceptor] postHandle");
    }

    /**
     * 渲染视图之后被调用. 释放资源
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("[FirstInterceptor] afterCompletion");
    }

}
FirstInterceptor.java

指定特定路径拦截器生效:

异常处理

SpringMVC 通过 HandlerExceptionResolver  处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。 SpringMVC 提供的 HandlerExceptionResolver 的实现类

HandlerExceptionResolver


DispatcherServlet  默认装配的 HandlerExceptionResolver :没有使用 <mvc:annotation-driven/> 配置:

–使用了 <mvc:annotation-driven/> 配置

 

 ExceptionHandlerExceptionResolver


主要处理 Handler 中用 @ExceptionHandler 注解定义的 方法。

---@ExceptionHandler 注解定义的方法优先级问题:例如发• 生的是NullPointerException,但是声明的异常有 RuntimeException 和 Exception,此候会根据异常的最近 继承关系找到继承深度最浅的那个 @ExceptionHandler 注解方法,即标记了 RuntimeException 的方法

---ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice 中的  @ExceptionHandler 注解方法

ResponseStatusExceptionResolver


在异常及异常父类中找到 @ResponseStatus 注解,然• 后使用这个注解的属性进行处理。 定义一个 @ResponseStatus 注解修饰的异常类•


若在处理器方法中抛出了上述异常:• 若ExceptionHandlerExceptionResolver 不解析述异常。由于 触发的异常 UnauthorizedException 带有@ResponseStatus 注解。因此会被ResponseStatusExceptionResolver 解析 到。最后响应HttpStatus.UNAUTHORIZED 代码给客户 端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。 关于其他的响应码请参考 HttpStatus 枚举类型源码。

 DefaultHandlerExceptionResolver


对一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException、HttpReques tMethodNotSupportedException、HttpMediaTypeNotSuppo rtedException、HttpMediaTypeNotAcceptableException 等。

    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) {

        try {
            if (ex instanceof NoSuchRequestHandlingMethodException) {
                return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
                        handler);
            }
            else if (ex instanceof HttpRequestMethodNotSupportedException) {
                return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
                        response, handler);
            }
            else if (ex instanceof HttpMediaTypeNotSupportedException) {
                return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
                        handler);
            }
            else if (ex instanceof HttpMediaTypeNotAcceptableException) {
                return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
                        handler);
            }
            else if (ex instanceof MissingServletRequestParameterException) {
                return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
                        response, handler);
            }
            else if (ex instanceof ServletRequestBindingException) {
                return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
                        handler);
            }
            else if (ex instanceof ConversionNotSupportedException) {
                return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
            }
            else if (ex instanceof TypeMismatchException) {
                return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
            }
            else if (ex instanceof HttpMessageNotReadableException) {
                return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
            }
            else if (ex instanceof HttpMessageNotWritableException) {
                return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
            }
            else if (ex instanceof MethodArgumentNotValidException) {
                return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);
            }
            else if (ex instanceof MissingServletRequestPartException) {
                return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);
            }
            else if (ex instanceof BindException) {
                return handleBindException((BindException) ex, request, response, handler);
            }
            else if (ex instanceof NoHandlerFoundException) {
                return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
            }
        }
        catch (Exception handlerException) {
            logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
        }
        return null;
    }
View Code

SimpleMappingExceptionResolver


如果希望对所有异常进行统一处理,可以使用 • SimpleMappingExceptionResolver,它将异常类名映射为 视图名,即发生异常时使用对应的视图报告异常

SpringMVC运行流程

在Spring环境下使用SpringMVC

<!--  
        需要进行 Spring 整合 SpringMVC 吗 ?
        还是否需要再加入 Spring 的 IOC 容器 ?
        是否需要再 web.xml 文件中配置启动 Spring IOC 容器的 ContextLoaderListener ?
        
        1. 需要: 通常情况下, 类似于数据源, 事务, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中).
        实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao. 
        2. 不需要: 都放在 SpringMVC 的配置文件中. 也可以分多个 Spring 的配置文件, 然后使用 import 节点导入其他的配置文件
    -->
    
    <!--  
        问题: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.
        解决:
        1. 使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分. 
        2. 使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解
    -->
    
    <!--  
        SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean. 
        返回来呢 ? 反之则不行. Spring IOC 容器中的 bean 却不能来引用 SpringMVC IOC 容器中的 bean!
    -->
View Code

Bean 被创建两次 ?

Spring 的 IOC 容器不应该扫描 SpringMVC 中的 bean, 对应的

SpringMVC 的 IOC 容器不应该扫描 Spring 中的 bean

啊哈;现在就只是创建一次的啦:

在 Spring MVC 配置文件中引用业务层的 Bean

SpringMVC的IOC容器中的bean可以用来引用SpringIOC容器中的bean,反之不行!

---多个 Spring IOC 容器之间可以设置为父子关系,• 以实现良好的解耦。

---Spring MVC WEB 层容器可作为 “业务层” Spring容器的子容器局部变量性质):即 WEB 层容器可以引用业务层 容器的 Bean,而业务层容器却访问不到 WEB 层 容器的 Bean

 

 handle(controller)依赖Service,而Service却不能依赖handle(controller)

SpringMVC对比Struts2

SpringMVC 对比 Struts2


①. Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter

②. Spring MVC 会稍微比 Struts2 快些. Spring MVC 是基于方法设计, 而 Sturts2 是基于类, 每次发一次请求都会实 例一个 Action.

③. Spring MVC 使用更加简洁, 开发效率Spring MVC确实• 比 struts2 高: 支持 JSR303, 处理 ajax 的请求更方便

④. Struts2 的 OGNL 表达式使页面的开发效率相比 • Spring MVC 更高些

乱码问题:

springmvc默认ISO-8859-1

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">

  <!-- 配置 SpringMVC 的 DispatcherServlet -->
  <!-- The front controller of this Spring Web application, responsible for handling all application requests -->

  <!--过滤器解决工程编码-注意最好在前面因为执行顺序->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext</param-value>
  </context-param>
  <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc/springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- Map all requests to the DispatcherServlet for handling -->
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- 配置 HiddenHttpMethodFilter: 把 POST 请求转为 DELETE、PUT 请求 -->
  <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/js/*</url-pattern>
    <url-pattern>/css/*</url-pattern>
    <url-pattern>/fonts/*</url-pattern>
    <url-pattern>/images/*</url-pattern>
  </servlet-mapping>

</web-app>
View Code
原文地址:https://www.cnblogs.com/biaogejiushibiao/p/9726944.html