Spring MVC详细的学习

Spring MVC

一、概述

1.1什么是SpringMVC

复习MVC

Spring MVC详细的学习

MVC提倡:每层编写自己的东西, 不写任何与该层无关的代码

分层的目的:为了解耦,分工明确,方便后期代码维护。

Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架

1.2Spring MVC的特点:
  • 轻量级,简单易学
  • 高效,基于请求响应的MVC框架
  • 与Spring兼容性好, 无缝结合
  • 约定优于配置
  • 功能强大: RESTful、 数据验证、格式化、本地化、主题等
  • 简洁灵活
1.3核心组件
  • DispatcherServlet: 前置控制器,负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Sprring MVC 的核心模块。
  • Handler: 处理器,完成具体的业务逻辑,相当于Servlet。
  • HandlerMapping: DispatcherServlet是通过HandlerMapping将请求映射到不同的Handler。
  • HandlerInterceptor: 处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成。
  • HandlerExecutionChain: 处理器执行链,包括两部分内容: Handler 和HandlerInterceptor (系统会有一个默认的HandlerInterceptor,如果需要额外拦截处理,可以添加拦截器进行设置)。
  • HandlerAdapter: 处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型的转换、将表单数据封装到POJO等,这一些列操作都是由
    HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler。
  • ModelAndView: 封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet。
  • ViewResolver: 视图解析器,DispatcherServlet 通过它将逻辑视图解析为物理视图,最终将渲染的结果响应给客户端。
1.4工作流程
  1. 客户端请求被DispatcherServlet接收。
  2. 根据HandlerMapping映射到Handler.
  3. 生成Handler和HandlerInterceptor。
  4. Handler 和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet。
  5. DispatcherServlet 通过HandlerAdpater调用Handler的方法完成业务逻辑处理。
  6. 返回一个ModelAndView对象给DispatcherServlet。
  7. DispatcherServlet 将获取的ModelAndView对象传给ViewResolver视图解析器,将逻辑视图解析成物理视图。
  8. ViewResolver返回一个View给DispatcherServlet。
  9. DispatcherServlet根据View进行视图渲染(将模型数据填充到视图中)
  10. DispatcherServlet将渲染之后的视图响应给客户端。

Spring MVC详细的学习

二.入门案列

2.1 流程梳理

1、DispatcherServlet 接收到URL请求index,结合@RequestMapping"/index")注解将该请求交给index业务方法进行处理。

2、执行index业务方法,控制台打印日志,并且返回"index"字符串(逻辑视图)。

3、结合springmvc.xml中的视图解析器配置,找到目标资源: /index.jsp, 即根目录下的index.jsp文件,将该JSP资源返回给客户端完成响应。

Spring MVC环境搭建成功。

1.导入相关依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.在web.xml中配置前端控制器

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!--DispatcherServlet 对象的创建是在使⽤时创建,如果需要在运⾏时创建,则需要通过load-on-startup 进⾏设置,数值越⼩,加载的优先级越⾼-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <!--
        / 拦截 所有请求
        /* 拦截所有请求,但是视图层的请求是不可以访问
        http://localhost:8080/springmvc/index.jsp
        http://localhost:8080/springmvc/hello
    -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

3.在 resources中创建SpringMVC配置文件:spring-mvc.xml

 <!--注解扫描-->
    <context:component-scan base-package="com.wdzl..controller"></context:component-scan>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

4.编写请求处理器,并表示为控制器,同时为类中的方法提供请求路径

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "/success.jsp";
    }
}
2.2 Spring MVC 常用注解

@RequestMapping常用参数

  • value :指定URL请求的实际地址,是@RequestMapping的默认值(也可以用path)
@RequestMapping(value = "/d1")
    public String index(){
        System.out.println("接收到了");
        return "index";
    }
  • method : 指定请求的method 类型,包括GET、POST、PUT、DELETE等。
//只能用post方法请求
    @RequestMapping(value = "/d1",method = RequestMethod.POST)
    public String index(){
        System.out.println("接收到了");
        return "index";
    }
  • params:指定request请求中必须包含的参数值,若不包含,无法调用该方法。
    • params={“userName”},表示请求参数必须是有userName
    • params={“age!=10”},表示请求参数中age不能为10。
 @RequestMapping(value = "/d1",params={"id=1","name=tom"})
    public String index(int id,String name){
        System.out.println(id+":"+name);
        System.out.println("接收到了");
        return "index";
    }
2.3 Spring MVC组件扫描

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"
       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.xsd">
    <!--注解扫描-->
    <context:component-scan base-package="com.wdzl..controller"></context:component-scan>
</beans>

方式二:

<?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"
       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.xsd">
    <!--注解扫描-->
     <context:component-scan base-package="com.wdzl">
          <context:include-filter type="annotation"                    expression="org.springframework.stereotype.Controller"/>
   	 </context:component-scan>
</beans>

三、 SpringMVC 的数据响应

3.1 页面跳转 - 直接返回字符串

直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀进行拼接,然后跳转。

1. 直接返回字符串
2. 返回带前缀的字符串

Spring MVC默认以转发的形式响应JSP,可以手动进行修改。

转发

@RequestMapping("/d1/t1")
    public String test(){
        return "forward:/index.jsp";
    }

相当于

    @RequestMapping("/d1/t2")
    public String test2(){
        return "index";
    }

重定向

@RequestMapping("/d1/t3")
    public String test3(){
        return "redirect:/index.jsp";
    }

设置重定向的时候不能写逻辑视图,必须写明资源的物理路径,如"redirect:/index.jsp"

注意:

​ 重定向的资源一定是具有外部访问权限的资源,比如“WEB-INF”外部是不能访问的

3.2 页面跳转 - 返回ModelAndView

从入门案例演示效果中,我们不难看出,跳转的资源和方式 与返回值有关,所以这次我们将Controller中的方法返回值进行修改,这次我们返回ModelAndView对象

方式一:

    /**
     * Model-模型:用于分装对象
     * View-视图:用于展示数据
     * @return
     */

    @RequestMapping("/hello/t1")
    public ModelAndView Test(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("success");
        modelAndView.addObject("msg","你好");
        return modelAndView;
    }

方式二:

    /**
     * Model-模型:用于分装对象
     * View-视图:用于展示数据
     * @return
     */

    @RequestMapping("/d1/tt")
    public ModelAndView tests(ModelAndView modelAndView){

        modelAndView.setViewName("success");
        modelAndView.addObject("msg","你好");
        return modelAndView;
    }

方式三

    @RequestMapping("/hello/t3")
    public String test5(Model model){
        model.addAttribute("name","这几年");
        return "success";
    }

注意

​ 要在jsp页面,要加入一个属性(是否忽略EL表达式)

isELIgnored="false

  • Model - 模型:用于封装数据
  • View - 视图:用于展示数据,功能类似于Servlet中的转发或重定向的功能
  • setViewName():设置跳转的视图名称。会与配置文件中视图解析器的前后缀进行拼接
  • addObject():设置模型数据,类似于Servlet中的setAttribute(),使用键值对进行存储。

3.3页面跳转-返回域对象

当我们需要使用传统域对象时,我们也可以将域对象作为参数进行传递。

@RequestMapping("/e/t1")
public String test(HttpServletRequest request){
    request.setAttribute("msg","天不生我李淳罡,剑道万古如长夜");
    return "success";
}

3.4 回写数据 - 返回字符串

方式1:使用HttpServletResponse对象

@RequestMapping(value = "/e/t6")
public void hello6(HttpServletResponse response) throws IOException {        
    response.getWriter().println("Hello SpringMVC");
}

方式2:使用注解方式

@RequestMapping(value = "/e1/t7")
@ResponseBody//不进行视图跳转,直接进行数据响应
public String hello7(){
    return "Hello SpringMVC";
}

3.5 回写数据 - 返回JSON字符串

方式1:直接返回JSON字符串

@RequestMapping("/e/t8")
@ResponseBody//不进行视图跳转,直接进行数据响应
public String hello8(){    
    return "{\"username\":\"ZhouJieLun\",\"age\":30}";
}

方式2:使用转换工具将对象转换为JSON字符串并返回

1.在pom.xml中导包

 <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.0</version>
        </dependency>

2.编写方法

方式一

    @RequestMapping("/e/t5")
    @ResponseBody
    public String test3() throws JsonProcessingException {
        User user = new User();
        user.setId(6);
        user.setName("哈哈哈");
        //使用JSON转换工具将对象转换成JSON格式的字符串并返回
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(user);
        return json;
    }

方式二

    @RequestMapping("/e/t6")
    @ResponseBody
    public User test4(){
        User user = new User();
        user.setId(6);
        user.setName("哈哈哈");
        return user;
    }

注意

​ 要在配置文件加入下面的配置

    <!-- 配置处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
            </list>
        </property>
    </bean>

方式三:

    @RequestMapping("/e/t6")
    @ResponseBody
    public User test4(){
        User user = new User();
        user.setId(6);
        user.setName("哈哈哈");
        return user;
    }

   <!--MVC注解驱动-->
    <mvc:annotation-driven/>

在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为MVC三大组件。使用<mvc:annotation>可以自动加载 处理器映射器 和 处理器适配器,并且默认底层会集成jackson进行对象或集合的JSON格式字符串的转换。

四、SpringMVC 获取请求参数

服务器端需要获取请求参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:

  • 基本数据类型
  • POJO类型
  • 数组类型
  • 集合类型

接下来我们分别去讲解这几类情况

  • @ResponseBody:这个注解配合**@Controller**使用 ,表示他不会走视图解析器,会直接返回一个字符串
  • @RestController:表示他不会走视图解析器,会直接返回一个字符串

4.1 获取请求 - 基本数据类型参数

在Controller中,只要业务方法的形参,与请求参数同名,SpringMVC就可以自动将请求参数封装到形参中。

1.编写业务方法

   @RequestMapping("/baseType")
    @ResponseBody
    public String baseType(int id){
        return "id:"+id;
    }

客户端HTTP请求中必须包含id参数,否则抛出500异常,因为id不能为null。

同时id的值必须为数值且必须为整数,否则抛出400异常。

4.2 获取请求 - POJO类型参数

在Controller中,在业务方法声明中,形参的属性与 请求参数同名,并提供相应 get、set 方法,SpringMVC会自动将参数封装到对象中。

pojo

public class User {
    private int id;
    private String name;
    private int age;
    //构造
    //get/set
    //toString()
}

//前端接收的是一个对象
    /**
     * 1.按收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
     *2.假设传递的是一个对 象User.四配User对象中的字段名:如果名字一致则OK, 否则,匹配不到
     */
    @RequestMapping("m1/t2")
    public String test2(User user){
        System.out.println(user);
        return "test";
    }

注意:如果使用对象的话,,前端传递的参数名和对象名必须一致,否则就是null。

练习

User

public class User {
    private int id;
    private String name;
    //构造
    //get/set
    //toString()
}

   @RequestMapping("/getUser")
    public String getUser(User user){
        System.out.println(user);
        return "index";
    }

级联操作

User

public class User {
    private int id;
    private String name;
    private Address address;
        //构造
    //get/set
    //toString()
}

Address

public class Address {
    private int code;
    private String message;
            //构造
    //get/set
    //toString()
}

add.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/getUser" method="post">
    <table>
        <tr>
            <td>ID</td>
            <td>
                <input type="text" name="id"/>
            </td>
        </tr>
        <tr>
            <td>姓名</td>
            <td>
                <input type="text" name="name"/>
            </td>
        </tr>
        <tr>
            <td>地址ID</td>
            <td>
                <input type="text" name="address.code"/>
            </td>
        </tr>

        <tr>
            <td>地址信息</td>
            <td>
                <input type="text" name="address.message"/>
            </td>
        </tr>

        <tr>

            <td>
                <input type="submit" name="提交"/>
            </td>
        </tr>
    </table>
</form>
</body>
</html>


Handler

    @RequestMapping("/getUser")
    public String test2(User user){
        System.out.println(user);
        return "i";
    }

4.3 获取请求 - 数组类型参数

在Controller中,只要业务方法的形参,与请求参数同名,SpringMVC就可以自动将请求参数封装到形参集合中。

controller:

@RequestMapping(value = "/hello13")
@ResponseBody
public void hello13(String[] name){
    System.out.println(Arrays.toString(name));
}

 @RequestMapping("/array")
    @ResponseBody
    public String arrayType(String[] names){
        StringBuffer buffer = new StringBuffer();
        for (String name : names){
            buffer.append(name).append(" ");
        }
        return "names:"+buffer.toString();
    }

4.4 获取请求 - 集合类型参数

集合List

Spring MVC不支持List类型的直接转换,需要包装成Object。

方式一

List的自定义包装类

public class UserList {
    private List<User> userList;
    //构造
    //get/set
    //toString()
}

业务方法

    @RequestMapping("/listType")
    @ResponseBody
    public String listType(UserList userList){
        StringBuffer buffer = new StringBuffer();
        for (User user : userList.getUserList()){
            buffer.append(user).append(" ");
        }
        return "userList:"+buffer.toString();
    }

JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/listType" method="post">
    用户1ID<input type="text" name="userList[0].id"><br>
    用户1名字<input type="text" name="userList[0].name"><br>
    用户2ID<input type="text" name="userList[1].id"><br>
    用户2名字<input type="text" name="userList[1].name"><br>
    <input type="submit">

</form>
</body>
</html>

需要注意的是User类一定 要有无参构造函数,否则抛出异常。

方式二:通过Ajax提交数据

ajax.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="/js/jquery-3.5.1.min.js"></script>
    <script>
        var userList = Array();
        userList.push({name:"废材",id:"10"});
        userList.push({name:"你好",id:"15"});
        userList.push({name:"世界",id:"20"});

        $.ajax({
            type:"POST",
            url:"/e/t10",
            data:JSON.stringify(userList),
            contentType:"application/json;charset=utf-8"
        })
    </script>
</head>
<body>

</body>
</html>

Controller

    @RequestMapping("/e/t10")
    @ResponseBody
    public void test8(@RequestBody List<User> userList){
        for (User user : userList){
            System.out.println(user);
        }
    }

4.5 获取请求 - 获取静态资源

​ 在上一案例中,如果没有配置<mvc:resources>之前,静态资源是不能被访问到的,为什么呢?

	我们先来回顾一下SpringMVC的工作原理。

	首先我们需要在web.xml中配置前端控制器,并且我们配置的访问路径是:“ / ”,代表所有的请求都会过前端控制器。

​ 接下来,当我们访问ajax.jsp时,该页面会自动提交json数据,但是在提交之前,需要先访问jquery.js文件,访问该文件的请求也会被前端控制器获取,并且前端控制器会误以为,该请求也是在请求某个处理器,所以就会报错。

<mvc:resources mapping="/js/**" location="/js/"/>

该配置相当于开放资源的访问,配置中的访问不需要通过前端控制器。

比如,后面我们网页会有大量的图片,我们也可以通过此种方式开放对图片的访问

<mvc:resources mapping="/img/**" location="/img/"/>

mapping 是访问资源的URL, location是资源具体存放的目录。

还有另外一种方式,当前端控制器找不到资源时,可以交给原始的控制器,也就是tomcat来寻找。

    <!--mvc注解驱动-->    
<mvc:annotation-driven/>
<!--    &lt;!&ndash;静态资源访问&ndash;&gt;    
<mvc:resources mapping="/js/**" location="/js/"/>-->    <mvc:default-servlet-handler/>

4.6 获取请求 - 处理中文乱码问题

当表单提交时,数据会出现乱码问题,我们可以通过设置一个过滤器来进行编码的过滤。

在web.xml中添加如下配置:注意过滤器要写在serlvet前面。

<!--配置全局过滤器--> 
<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> 
</filter> 
<filter-mapping>   
    <filter-name>CharacterEncodingFilter</filter-name>   
    <url-pattern>/*</url-pattern> 
</filter-mapping>Copy

此时再提交表单就不会有中文乱码问题了。

4.7 参数绑定参数:@RequestParam

1、在业务方法定义时声明参数列表。

2、给参数列表添加@RequestParam注解进行绑定。

  • 提交的域名称和处理方法的参数名一致
    //http://localhost:8080/m1/t1?name=xxx

    @RequestMapping("/m1/t1")
    public String test(String name , Model model){
        //1.接收前端数据
        System.out.println("接收到的数据:"+name);
        //2.将返回的结果传递给前端
        model.addAttribute("msg",name);
        //3.试图跳转
        return "test";
    }

  • 提交的域名称和处理方法的参数名不一致
//http://localhost:8080/m1/t1?username=xxx    
//@RequestParam("username") : username提交的域的名称
@RequestMapping("/m1/t1")
    public String test(@RequestParam("username") String name , Model model){
        //1.接收前端数据
        System.out.println("接收到的数据:"+name);
        //2.将返回的结果传递给前端
        model.addAttribute("msg",name);
        //3.试图跳转
        return "test";
    }

Spring MVC可以自动完成数据类型转换,该工作是由HandlerAdapter来完成的。

@RequestParam 常用参数

  • value : 与请求参数同名
  • required:指定请求参数是否必须包含,默认为true,提交时如果不包含则报错
  • defaultValue:当没有指定请求参数时,则使用指定的默认值进行赋值
@RequestMapping(value = "/hello16")
@ResponseBodypublic void hello16(@RequestParam(value = "username",defaultValue = "哈哈") String name) {
    System.out.println(name);
}

4.8Spring MVC也支持RESTful风格的URL参数获取

Restful 是一种软件 架构风格 或 设计风格,而不是标准,它只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这种风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。

Restful风格的请求是使用 url + 请求方式 来表示一次请求目的的,HTTP协议里面四个表示操作方式

  • GET:用于获取资源
  • POST:用于新建资源
  • PUT:用于更新资源
  • DELETE:用于删除资源
    //原来的 : http://localhost:8080/SpringMVC/add?a=1&b=3

    @RequestMapping("/add")
    public String test(int a, int b, Model model){
        int res = a + b;
        model.addAttribute("msg","结果1为:"+res);
        return "hello";

    }

    // RestFul : http://localhost:8080/SpringMVC/add/2/5

    //@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
    //@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.POST)
    //@PostMapping
    //@DeleteMapping
    //@PutMapping
    @GetMapping("add/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable int b, Model model){
        int res = a + b;
        model.addAttribute("msg","结果2为:"+res);
        return "hello";

    }

    @PostMapping("add/{a}/{b}")
    public String test3(@PathVariable int a, @PathVariable int b, Model model){
        int res = a + b;
        model.addAttribute("msg","结果3为:"+res);
        return "hello";

    }

映射Cookie

    //映射Cookie
    @RequestMapping("/cookie")
    public String getCookie(@CookieValue("JSESSIONID") String sessionId){
        System.out.println(sessionId);
        return "index";
    }

4.9 自定义类型转化器

在前面hello11() 方法演示中,虽然我们输入的age是数字,但是传输到服务器端都会变成字符串。在二阶段时,Servlet获取请求参数使用了一个方法叫 getParameter() 该方法返回的数据类型都是String就足以证明无论前端传输的数据类型是什么,最终到达服务器端都是字符串格式。

SpringMVC默认提供了一些常用的类型转换器,例如:客户端提交的字符串转换成相应的int类型。但是并不是所有数据类型都提供了转换器,如果没有提供就需要我们手动的定义转换器。比如日期类型的数据就需要我们自定义转换器。

实现步骤:

1. 定义转换器类实现Converter接口
2. 在配置文件中声明转换器
3. 在`<annotation-driven>`中引用转换器

Converter

/**
 * 自定义转换器:String -> Date
 */
public class DateConverter implements Converter<String ,Date> {

    @Override
    public Date convert(String s) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}


Controller

    @RequestMapping("/e/t11")
    @ResponseBody
    public void test9(Date date){
        System.out.println(date);
    }

配置文件

   <!--MVC注解驱动-->
    <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
    <!--声明转换器-->
    <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.wdzl.converter.DateConverter"></bean>
            </list>
        </property>
    </bean>

4.10 Servlet相关API

@RequestMapping(value = "/e/t13")
@ResponseBody
public void hello19(HttpServletRequest request , HttpServletResponse response, HttpSession session) {
    System.out.println(request);    
    System.out.println(response);    
    System.out.println(session);
}

4.11 获取请求头

通过使用@RequestHeader可以获取请求头信息,相当于web阶段学习的request.getHeader(name)

@RequestMapping(value = "/e/t14")
@ResponseBodypublic void hello20(@RequestHeader(value="Host") String host,                    @RequestHeader(value ="User-Agent") String user_agent) {    
    System.out.println(host);    
    System.out.println(user_agent);
}

这里需要指出的是头信息信息中有一个比较特殊的值:cookie,它需要另一个注解来取@CookieValue

@RequestMapping(value = "/e/t15")
@ResponseBodypublic void hello20(@RequestHeader(value="Host") String host,@RequestHeader(value ="User-Agent") String user_agent,@RequestHeader(value = "Cookie") String cookie,    @CookieValue(value = "JSESSIONID") String jsessionid) {    
    System.out.println(host);    
    System.out.println(user_agent);    
    System.out.println(cookie);//取出Cookie对应的所有键值对    
    System.out.println(jsessionid);
}

通过@CookieValue可以将 cookie中存储的键值对的值取出,相当于嵌套Map集合取内层Map的value

五、文件上传

三要素

  • 表单项:input type=“file”
  • 表单提交方式:POST
  • 表单enctype属性为"multipart/form-data"

导入依赖

<!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>

单个文件上传

JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/file/upload" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="name">
    文件:<input type="file" name="upload">
    <input type="submit" value="提交">
</form>
</body>
</html>


配置文件需要文件解析器

 <!--文件上传解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的总大小-->
        <property name="maxUploadSize" value="10240000"></property>
        <!--设置单个上传文件的大小-->
        <property name="maxUploadSizePerFile" value="102400"></property>
        <!--设置编码格式-->
        <property name="defaultEncoding" value="UTF-8"></property>
    </bean>

Controller

    @RequestMapping("/upload")
    @ResponseBody
    public void upload(String name , MultipartFile upload) throws IOException {
        String filename = upload.getOriginalFilename();
        //上传
        upload.transferTo(new File("C:\\Users\\Public\\Pictures\\NVIDIA Corporation\\"+filename));

    }

多个文件上传

JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/file/uploads" method="post" enctype="multipart/form-data">
    文件1:<input type="file" name="uploads"><br>
    文件2:<input type="file" name="uploads"><br>
    文件3:<input type="file" name="uploads"><br>
    <input type="submit" value="提交">
</form>

</body>
</html>

Controller

@RequestMapping("/uploads")
    @ResponseBody
    public void uploads(@RequestParam("uploads") MultipartFile[] uploads){
        for (MultipartFile upload : uploads){
            if (upload.getSize()>0){
                String filename = upload.getOriginalFilename();//获取文件名
                //上传
                try {
                    upload.transferTo(new File("D:\\C盘的应用\\"+filename));
                    System.out.println("OK");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

六、拦截器 - Interceptor

概述:

SpringMVC的拦截器类似于Servlet开发中的过滤器Fileter , 用于处理器进行预处理 和 后处理。

将拦截器按照一定顺序 连接成一条链,这条链称为 拦截器链(Interceptor Chain)。在访问被拦截的方法和字段时,拦截器链中的拦截器就会按照定义的顺序被调用。拦截器也是AOP思想的实现。

拦截器和过滤器的区别:

区别 过滤器 拦截器
使用范围 是Servlet规范中的一部分,任何Web工程都可以使用。 属于SpringMVC,只有使用该框架才能够使用拦截器。
拦截范围 在中配置了/*之后,可以对所有要访问的资源拦截。 只会拦截访问控制器的方法,如果访问的是JSP,html,css,image,js是不会进行拦截的。

实现步骤:

  1. 创建拦截器类,实现HandlerInterceptor接口
  2. 配置拦截器
  3. 测试拦截器的拦截对象

1.创建拦截器

package com.wdzl.interceptor;


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

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

/**
 * 拦截器类
 * @author lp
 * @version 1.0
 */
public class MyInterceptor implements HandlerInterceptor {
    //在目标方法执行前,被执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");
        return true;
    }

    //在目标方法还行之后,视图返回之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }
    //在视图返回后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}


2.spring-mvc.xml中配置拦截器

<!--配置拦截器-->    
<mvc:interceptors>        
    <mvc:interceptor>            
        <mvc:mapping path="/**"/>            
        <bean class="com.wdzl.interceptor.MyInterceptor"/>           </mvc:interceptor>  
</mvc:interceptors>

Spring MVC详细的学习

6.2 方法详解

preHandle

概述:

​ preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处

理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在多个

Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有

的Interceptor中的preHandle方法都会在Controller方法调用之前调用。

​ SpringMVC的这种Interceptor链式结构也是可以进行中断的, 这种中断方式

是令preHandle的返回值为false,当preHandle的返回值为false的时候整个请求就

结束了。

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle...");
    String flag = request.getParameter("flag");
    if (flag.equals("ok")) {
        return true;
    } else {
        request.getRequestDispatcher("../error.jsp").forward(request,response);
        return false;
    }
}

测试:

新建一个error.jsp

<html>
    <head>    
        <title>Title</title>
    </head>
    <body>
        <h1>系统正在维护。。。</h1>
    </body>
</html>

地址栏输入访问地址:

http://localhost:8080/user/show?flag=no ----> 显示 系统正在维护。。。
http://localhost:8080/user/show?flag=yes ----> 显示 index的内容

2.postHandle

概述:

postHandle是进行处理器拦截用的,它的执行在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用。

演示:

//在目标方法还行之后,视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        
    modelAndView.addObject("userName", "林俊杰");        
    System.out.println("postHandle...");    
}

测试:

跳转成功后,页面本身的userName属性值会被该方法中的新属性值替代。

3.afterCompletion

  该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 这个方法的主要作用是用于清理资源的。

4.多个拦截器执行顺序

多个拦截器的执行顺序和 他们在 spring-mvc.xml 配置顺序有关。

情景1:

Spring MVC详细的学习

情景2:

Spring MVC详细的学习

情景3:

Spring MVC详细的学习

从上述图示,我们可以得出一个结论:

如果拦截器的preHandle返回true,则该拦截器的afterCompletion方法一定会被调用。

七、 异常处理

7.1 简单异常处理器SimpleMappingExceptionResolver

SpringMVC定义好了该处理器,在使用时可以根据项目情况进行相应异常与视图的映射配置。

1.新建一个module,导入相应依赖

2.创建spring-mvc.xml配置文件

3.在web.xml中配置前端控制器和全局过滤器

4.创建Controller

/** 
*  用户控制层
* @author lp
* @version 1.0
*/
@Controller
@RequestMapping("/user")
public class UserController {    
    @RequestMapping("/ex")    
    public String exceptionTest(){        
        System.out.println(3/0);//运行时异常        
        return "index";    
    }
}

5.测试

访问Controller,因为方法中有运行时异常,所以页面会直接报500错误,这样对于用户体验是不好的。所以我们需要将异常进行一个处理,给用户一些更友好的提示。

6.配置简单异常处理器

<!--简单异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">    
    <property name="defaultErrorView" value="error"/>
</bean>

其中 value 属性值就是捕获异常后,将要跳转的视图名称。此时error.jsp还不存在,所以我们去创建一个相应的视图

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>    
        <title>Title</title>
    </head>
    <body>
        <h1>系统正在维护中。。。。</h1>
    </body>
</html>

7.再次测试,此时页面就不会显示500的错误,而是显示我们配置好的异常页面,这样对于用户体验度来说会好很多。

当然对于异常我们也可以采取差异化处理。

8.在spring-mvc.xml中配置

<!--简单异常处理器-->   
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">      
    <property name="exceptionMappings">          
        <map>              
            <entry key="java.lang.ClassCastException" value="error2"></entry>              
            <entry key="java.lang.Exception" value="error"></entry>          
        </map>      
    </property>   
</bean>

9.复制一个error2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>    
        <title>Title</title>
    </head>
    <body>
        <h1>类型转换异常。。。。</h1>
    </body>
</html>

10.在控制层添加各种异常

@Controller
@RequestMapping("/user")
public class UserController {    
    @RequestMapping("/ex")    
    public String exceptionTest(){
        //System.out.println(3/0);        
        Object name = "周杰伦";        
        System.out.println((int)name);        
        return "index";    
    }
}

11.测试

在地址栏输入访问地址,可以通过每次页面显示的内容来确定异常是否被差异化处理。

7.2 自定义异常处理

实现步骤:

  1. 创建异常处理类实现HandlerExceptionResolver
  2. 创建异常处理器
  3. 编写异常页面
  4. 测试

演示:

1.创建自定义异常处理类

/** 
* 自定义异常处理类 
* @author lp 
* @version 1.0
*  实现步骤: 
*      1. 创建异常处理类实现HandlerExceptionResolver 
*      2. 创建异常处理器 
*      3. 编写异常页面 
*      4. 测试 
*/
public class MyExceptionResolver implements HandlerExceptionResolver {    
    /**     
    *     
    * @param request     
    * @param response     
    * @param handler 执行的处理程序,如果在发生异常时未选择任何处理程序,则为null     
    * @param ex 异常对象     
    * @return ModelAndView 跳转到错误视图的信息     
    */    
    @Override    
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {        
        ModelAndView modelAndView = new ModelAndView();        
        if(ex instanceof ClassCastException){            
            modelAndView.addObject("msg", "异常转换异常");        
        } else if (ex instanceof Exception) {            
            modelAndView.addObject("msg", "其他异常");        
        }        
        modelAndView.setViewName("error");        
        return modelAndView;    
    }
}

2.在主配置文件中配置

<!--自定义异常处理器-->
<bean class="com.wdzl.resolver.MyExceptionResolver"></bean>
上一篇:SpringMVC学习笔记


下一篇:浅谈@GetMapping、@PostMapping和@RequestMapping注解的区别和使用