SpringMVC学习笔记

目录

SpringMVC简介

SpringMVC概述

SpringMVC快速入门

开发步骤

代码实现

SpringMVC流程图示

SpringMVC的组件解析

SpringMVC的执行流程

SpringMVC组件解析

1. 前端控制器:DispatcherServlet

2. 处理器映射器:HandlerMapping

3. 处理器适配器:HandlerAdapter

4. 处理器:Handler

5. 视图解析器:View Resolver

6. 视图:View

SpringMVC注解解析

@RequestMapping

SpringMVC组件扫描

mvc命名空间引入

组件扫描

XML配置解析(资源解析器)

SpringMVC的数据响应

SpringMVC的数据响应方式

页面跳转

直接返回字符串

通过ModelAndView对象返回-应用1

通过ModelAndView对象返回-应用2

通过ModelAndView对象返回-应用3

回写数据

直接返回字符串-应用1(返回普通字符串)

直接返回字符串-应用2(返回json格式字符串)

返回对象或集合-应用1(返回对象)

返回对象或集合-应用2(返回集合)

返回对象或集合-应用3(简化配置)

SpringMVC的请求

获得请求参数

获得基本类型参数

获得POJO类型参数

获得数组类型参数

获得集合类型参数

应用1-使用VO实体类

应用2-使用Ajax提交json格式数据

静态资源访问的开启

配置全局乱码过滤器

参数绑定注解@RequestParam

Restful风格的参数的获取

自定义类型转换器

获得Servlet相关API

获得请求头信息

SpringMVC的文件上传

客户端表单实现

文件上传的原理

单文件上传的代码实现

多文件上传的代码实现

SpringMVC的拦截器

拦截器的作用

interceptor和filter区别

快速入门

快速入门详解

拦截器方法说明

SpringMVC异常处理机制

异常处理的思路

异常处理两种方式

简单异常处理器SimpleMappingExceptionResolver

自定义异常处理步骤


SpringMVC简介

SpringMVC概述

SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。

SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。

总结:SpringMVC是一款基于MVC的web框架,可通过注解处理请求,同时支持 RESTful 编程风格的请求。

SpringMVC快速入门

开发步骤

①导入SpringMVC相关坐标

②配置SpringMVC核心控制器DispathcerServlet

③创建Controller类和视图页面

④使用注解配置Controller类中业务方法的映射地址

⑤配置SpringMVC核心文件 spring-mvc.xml

⑥客户端发起请求测试

代码实现

①在maven的pom.xml文件中导入Spring和SpringMVC的坐标、也导入Servlet和Jsp的坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

②在web.xml配置SpringMVC的前端控制器,其中<init-param>标签配置初始化参数,告诉spring-mvc.xml文件的位置。

注意,<param-name>标签的值是固定的:contextConfigLocation

    <!--配置SpringMVC的前端控制器-->
    <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>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

③创建Controller和业务方法

public class UserController {
    public String save(){
        System.out.println("Controller ");
        return "success.jsp";
    }
}

③创建视图页面success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>Success</h1>
</body>
</html>

④配置注解

@Controller// 把该类放到Spring容器中
public class UserController {
    @RequestMapping("/quick")
    public String save(){
        System.out.println("Controller ");
        return "success.jsp";
    }
}

⑤创建spring-mvc.xml,并配置组件扫描(因为配置这些注解后,spring并不清楚所以需要进行组件扫描)

<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd 
    http://www.springframework.org/schema/mvc   
    http://www.springframework.org/schema/mvc/spring-mvc.xsd  
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置注解扫描-->
    <context:component-scan base-package="com.demo"/>
</beans>

⑥访问测试地址

浏览器页面显式:

控制台打印:

SpringMVC流程图示

SpringMVC的流程图如下:

从代码角度看:

SpringMVC的组件解析

SpringMVC的执行流程

  • ①用户发送请求至前端控制器DispatcherServlet。
  • ②DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  • ③处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  • ④DispatcherServlet调用HandlerAdapter处理器适配器。
  • ⑤HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  • ⑥Controller执行完成返回ModelAndView。
  • ⑦HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
  • ⑧DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  • ⑨ViewReslover解析后返回具体View。
  • ⑩DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

SpringMVC组件解析

1. 前端控制器:DispatcherServlet

用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。

2. 处理器映射器:HandlerMapping

HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3. 处理器适配器:HandlerAdapter

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4. 处理器:Handler

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。

5. 视图解析器:View Resolver

View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

6. 视图:View

SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面

SpringMVC注解解析

@RequestMapping

作用:用于建立请求 URL 和处理请求方法之间的对应关系。

位置:

  • 类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
  • 方法上,请求 URL 的第二级访问目录,与类上的使用@RequestMapping标注的一级目录一起组成访问虚拟路径

属性:

  • value:用于指定请求的URL,如果只写一个value属性,那么可以省略。它和path属性的作用是一样的
  • method:用于指定请求的方式,GET、POST等请求方式,值为枚举如RequestMethod.GET。
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样

例如:

  • params = {"accountName"},表示请求参数必须有accountName
  • params = {"moeny!100"},表示请求参数中money不能是100

SpringMVC组件扫描

mvc命名空间引入

命名空间:
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
约束地址:
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd

基础的spring-mvc.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        
            http://www.springframework.org/schema/beans/spring-beans.xsd         
            http://www.springframework.org/schema/context 
            https://www.springframework.org/schema/context/spring-context.xsd">

</beans>

组件扫描

SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用下面的代码

<context:component-scan base-package=“com.demo.controller"/>

进行组件扫描。

这里spring的组件扫描和spring-mvc的组件扫描是分开扫描的。

XML配置解析(资源解析器)

SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

翻看该解析器源码,可以看到该解析器的默认设置,如下:

REDIRECT_URL_PREFIX = "redirect:"  --重定向前缀
FORWARD_URL_PREFIX = "forward:"    --转发前缀(默认值)
prefix = "";     --视图名称前缀
suffix = "";     --视图名称后缀

因此可以通过属性注入的方式修改视图的前后缀,即以前的转发地址可能是"/jsp/login.jsp",现在对前后缀进行配置后,只需要写"login"即可。

    <!--配置内部资源视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--原来是return "/jsp/success.jsp",现在是return "success"-->
        <!--配置前缀-->
        <property name="prefix" value="/jsp/"></property>
        <!--配置后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>

配置前后缀之前:

配置前后缀之后:

SpringMVC的数据响应

SpringMVC的数据响应方式

springmvc的数据响应方式有两种:一种是页面跳转,另一种是回写数据在浏览器页面上。

(1)页面跳转

  • 直接返回字符串,该字符串是页面路径
  • 通过ModelAndView对象返回

(2)回写数据

  • 直接返回字符串,该字符串是要返回的数据,如JSON格式的字符串数据
  • 返回对象或集合

页面跳转

直接返回字符串

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

返回带有前缀的字符串:

  • 转发:forward:/WEB-INF/views/index.jsp
  • 重定向:redirect:/index.jsp

通过ModelAndView对象返回-应用1

在Controller中方法返回ModelAndView对象,并且通过setViewName设置视图名称 。

    @RequestMapping("/quick2")
    public ModelAndView save2(HttpServletRequest request){
        /**
         * Model:模型,作用是封装数据
         * View:视图,作用是展示数据
         */
        ModelAndView modelAndView=new ModelAndView();
        // addObject(attributeName,attributeValue)表示设置模型数据,attributeName为键名,attributeValue为键值
        // request.setAttribute("username","张三");// 等价于下面一行的代码
        modelAndView.addObject("username","张三");
        // setViewName(viewName)表示设置视图名称,而viewName表示视图名称
        // 由于在spring-mvc.xml中配置了前后缀的,所以是"success"即可,如果没有设置,则是"/jsp/success.jsp"
        modelAndView.setViewName("success");
        return modelAndView;
    }

通过ModelAndView对象返回-应用2

在Controller中方法形参上直接声明ModelAndView,无需在方法中自己new一个,在方法中直接使用该对象设置视图,同样可以跳转页面 。

    @RequestMapping(value="/quick4")
    public String save4(Model model){
        // 而且不用实例化Model
        // request.setAttribute("username","张三");// 等价于下面一行的代码
        model.addAttribute("username","张三");
        return "success";
    }

    @RequestMapping("/quick3")
    public ModelAndView save3(ModelAndView modelAndView){
        // modelAndView作为参数,而不是ModelAndView modelAndView=new ModelAndView();
        modelAndView.addObject("username","张三");
        modelAndView.setViewName("success");
        return modelAndView;
    }

通过ModelAndView对象返回-应用3

在Controller方法的形参上可以直接使用原生的HttpServeltRequest对象、HttpSession对象等,只需声明即可,但不常使用。

    @RequestMapping("/quick5")
    public String save5(HttpServletRequest request, HttpSession session){
        request.setAttribute("username","张三");
        session.setAttribute("username","张三");
        return "success";
    }

回写数据

直接返回字符串-应用1(返回普通字符串)

通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为void。

将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。

    @RequestMapping("/quick7")
    @ResponseBody  //告知SpringMVC框架 不进行视图跳转 直接进行数据响应
    public String save7(){
        return "Hello SpringMVC!"
    }

    // 等价,两个方法的代码

    @RequestMapping("/quick6")
    public void save6(HttpServletResponse response) throws IOException {
        response.getWriter().println("Hello SpringMVC!");
    }

直接返回字符串-应用2(返回json格式字符串)

可以直接返回json格式的字符串。

    @RequestMapping("/quick8")
    @ResponseBody
    public String save8() {
        return "{\"username\":\"张三\",\"age\":18}";
    }

手动拼接json格式字符串的方式很麻烦,开发中往往要将复杂的java对象转换成json格式的字符串,我们可以使用web阶段学习过的json转换工具jackson进行转换,通过jackson转换json格式字符串,回写字符串。

在使用之前需要导入相关的坐标。

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

然后才是代码

    @RequestMapping("/quick9")
    @ResponseBody
    public String save9() throws IOException {
        User user = new User();
        user.setUsername("张三");
        user.setAge(30);
        //使用json的转换工具将对象转换成json格式字符串在返回
        ObjectMapper objectMapper = new ObjectMapper();
        // 转换成字符串
        String json = objectMapper.writeValueAsString(user);
        return json;
    }

返回对象或集合-应用1(返回对象)

上面每次都要执行下面两句将对象转换成JSON格式字符串,很麻烦,期望springmvc自动将对象转换成JSON格式的字符串:

//使用json的转换工具将对象转换成json格式字符串在返回
ObjectMapper objectMapper = new ObjectMapper();
// 转换成字符串
String json = objectMapper.writeValueAsString(user);

通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:

    <!--配置对对象或集合进行json字符串的转换并回写-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
            </list>
        </property>
    </bean>

然后是代码

    @RequestMapping("/quick10")
    @ResponseBody
    //期望SpringMVC自动将User转换成json格式的字符串
    public User save10() {
        User user = new User();
        user.setUsername("李四");
        user.setAge(32);
        return user;
    }

返回对象或集合-应用2(返回集合)

返回集合和上面的配置是一样的。

    @RequestMapping("/quick11")
    @ResponseBody//期望SpringMVC自动将User转换成json格式的字符串
    public List<User> save11() {
        User user = new User();
        user.setUsername("李四");
        user.setAge(32);
        List<User> list=new ArrayList<>();
        for(int i=0;i<5;i++){
            list.add(user);
        }
        return list;
    }

返回对象或集合-应用3(简化配置)

在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用mvc的注解驱动代替上述配置

<mvc:annotation-driven/>

在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。

使用<mvc:annotation-driven />自动加载 RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter( 处 理 适 配 器 ),可用在Spring-xml.xml配置文件中使用<mvc:annotation-driven />替代注解处理器和适配器的配置。

同时使用<mvc:annotation-driven />默认底层就会集成jackson进行对象或集合的json格式字符串的转换。

SpringMVC的请求

获得请求参数

客户端请求参数的格式是:name=value&name=value… …

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

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

获得基本类型参数

Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。并且能自动做类型转换。

自动的类型转换是指从String向其他类型的转换,比如说从String转向int类型。

请求地址是这种类型的:

http://localhost:8080/demo_springmvc1/quick9?username=zhangsan&age=12

而在方法的参数列表中,形参名称必须同请求地址中的参数名一致。

    @RequestMapping(value="/quick12")
    @ResponseBody
    public void save12(String username,int age) {
        System.out.println(username);
        System.out.println(age);
    }

浏览器页面显式:

控制台打印:

获得POJO类型参数

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。

创建一个实体类User.java

package com.demo.domain;

public class User {
    private String username;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

然后在方法内的形参中写实体类名即可,代码如下:

    @RequestMapping(value = "/quick13")
    @ResponseBody
    public void save13(User user) throws IOException {
        System.out.println(user);
    }

获得数组类型参数

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。

    @RequestMapping(value="/quick14")
    @ResponseBody
    public void save14(String[] strs) throws IOException {
        System.out.println(Arrays.asList(strs));
    }

浏览器页面:

控制台打印:

获得集合类型参数

应用1-使用VO实体类

获得集合参数时,要将集合参数包装到一个POJO中才可以。

index.jsp

<form action="/quick15" method="post">
    <%--表明是第一个User对象的username age--%>
    <input type="text" name="userList[0].username"><br/>
    <input type="text" name="userList[0].age"><br/>
    <input type="text" name="userList[1].username"><br/>
    <input type="text" name="userList[1].age"><br/>
    <input type="submit" value="提交">
</form>

然后还需要重新创建一个存放集合的实体类VO.java

package com.demo.domain;

import java.util.List;

public class VO {
    private List<User> userList;

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }

    @Override
    public String toString() {
        return "VO{" +
                "userList=" + userList +
                '}';
    }
}

然后才是在Controller中进行请求

    @RequestMapping(value="/quick15")
    @ResponseBody
    public void save15(VO vo) throws IOException {
        System.out.println(vo);
    }

应用2-使用Ajax提交json格式数据

当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装

<script src="/js/jquery-3.3.1.js"></script>
<script>
    var userList = new Array();
    userList.push({username: "zhangsan", age: 18});
    userList.push({username: "lisi", age: 28});

    $.ajax({
        type: "POST",
        url: "/quick16",
        data: JSON.stringify(userList),
        contentType: "application/json;charset=utf-8"
    });

</script>

然后是在Controlle中r获取从Ajax提交的数据。

    @RequestMapping(value="/quick16")
    @ResponseBody
    public void save15(@RequestBody List<User> userList) {
        System.out.println(userList);
    }

静态资源访问的开启

当有静态资源需要加载时,比如jquery.js文件或图片等静态资源,通过谷歌开发者工具抓包发现,没有加载到jquery文件,原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是/,代表对所有的资源都进行过滤操作。

我们可以通过以下两种方式指定放行静态资源:

  • 在spring-mvc.xml配置文件中指定放行的资源
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/img/**" location="/img/"/>
  • 在spring-mvc.xml配置文件中使用<mvc:default-servlet-handler/>标签
<mvc:default-servlet-handler/>

配置全局乱码过滤器

当post请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤。

在web.xml中进行配置:

    <!--配置全局过滤的filter-->
    <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>

参数绑定注解@RequestParam

当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定 。

<form action="${pageContext.request.contextPath}/quick17" method="post">
    <input type="text" name="name"><br>
    <input type="submit" value="提交"><br>
</form>
    @RequestMapping(value="/quick17")
    @ResponseBody
    public void save16(@RequestParam(value="name",required = false,defaultValue = "默认值") String username) {
        System.out.println(username);
    }

Restful风格的参数的获取

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

Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:

  • GET:用于获取资源
  • POST:用于新建资源
  • PUT:用于更新资源
  • DELETE:用于删除资源

例如:

/user/1 GET : 得到 id = 1 的 user

/user/1 DELETE: 删除 id = 1 的 user

/user/1 PUT: 更新 id = 1 的 user

/user POST: 新增 user

上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。

在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。

例如:http://localhost:8080/demo_springmvc/quick18/zhangsan

    @RequestMapping(value="/quick18/{name}")
    @ResponseBody
    public void save17(@PathVariable(value="name") String username) {// 可以设置method来规定请求方式是PUT、GET等
        System.out.println(username);
    }

自定义类型转换器

SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。

基本类型转换器
转换器描述
StringToBooleanConverterString到boolean类型转换。
ObjectToStringConverterObject到String转换,调用toString方法转换。
StringToNumberConverterFactoryString到数字转换(例如Integer、Long等)。
NumberToNumberConverterFactory数字子类型(基本类型)到数字类型(包装类型)转换。
 
StringToCharacterConverterString到Character转换,取字符串中的第一个字符。
NumberToCharacterConverter数字子类型到Character转换。
CharacterToNumberFactoryCharacter到数字子类型转换。
StringToEnumConverterFactoryString到枚举类型转换,通Enum.valueOf将宇符串转换为需要的枚举类型。
EnumToStringConverter枚举类型到String转换,返回枚举对象的name值。
StringToLocaleConverterString到java.util.Locale转换。
PropertiesToStringConverterjava.util.Properties到String转换,默认通过ISO-8859-1解码
StringToPropertiesConverterString到java.util.Properties转换,默认使用ISO-8859-1编码。
集合、数组相关转换器
转换器描述
ArrayToCollectionConverter任意数组到任意集合(List、Set)转换。
CollectionToArrayConverter任意集合到任意数组转换。
ArrayToArrayConverter任意数组到任意数组转换。
CollectionToCollectionConverter集合之间的类型转换。
MapToMapConverterMap之间的类型转换。
ArrayToStringConverter任意数组到String转换。
StringToArrayConverter字符串到数组的转换,默认通过“,“分割,且去除字符串两边的空格(trim)。
ArrayToObjectConverter任意数组到Object的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回数组的第一个元素并进行类型转换。
ObjectToArrayConverterObject到单元素数组转换。
CollectionToStringConverter任意集合(List、Set)到String转换。
StringToCollectionConverterString到集合(List、Set)转换,默认通过“,“分割,且去除字符串两边的空格(trim)。
CollectionToObjectConverter任意集合到任意Object的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回集合的第一个元素并进行类型转换。
ObjectToCollectionConverterObject到单元素集合的类型转换。

但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。

自定义类型转换器的开发步骤:

  • 第一步,定义转换器类实现Converter接口
  • 第二步,在配置文件中声明转换器
  • 第三步,在<annotation-driven>中引用转换器

需要一个类实现Converter<S,T>接口,其中泛型中前者表示原数据类型,也就是输入的数据类型,而后者表示要转换成的新数据类型,比如说将一个日期字符串转换成一个Date类型。

public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        // 其中s是输入的原数据类型
        //将日期字符串转换成日期对象 返回
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = format.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

接着是在spring-mvc.xml中进行声明

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.demo.converter.DateConverter"/>
            </list>
        </property>
    </bean>

最后是在<annotation-driven>标签中进行引用:

<mvc:annotation-driven conversion-service="conversionService" />

然后是应用

    @RequestMapping("quick19")
    @ResponseBody
    public void save19(Date date){
        System.out.println(date);
    }

测试,浏览器页面输入一个日期

控制台打印结果:

获得Servlet相关API

SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • ......
    @RequestMapping("quick20")
    @ResponseBody
    public void save20(HttpServletRequest request,HttpServletResponse response,HttpSession session){
        System.out.println(request);
        System.out.println(response);
        System.out.println(session);
    }

获得请求头信息

1、使用@RequestHeader可以获得请求头信息,相当于web阶段学习request.getHeader(name)

@RequestHeader注解的属性如下:

  • value:请求头的名称
  • required:是否必须携带此请求头
    @RequestMapping(value="/quick21")
    @ResponseBody
    public void save21(@RequestHeader(value = "User-Agent",required = false) String user_agent) {
        System.out.println(user_agent);
    }

2、使用@CookieValue可以获得指定Cookie的值

@CookieValue注解的属性如下:

  • value:指定cookie的名称
  • required:是否必须携带此cookie
    @RequestMapping(value="/quick22")
    @ResponseBody
    public void save22(@CookieValue(value = "JSESSIONID") String jsessionId) {
        System.out.println(jsessionId);
    }

SpringMVC的文件上传

客户端表单实现

文件上传客户端表单需要满足:

  • 表单项<input>标签的type属性值为type=“file”
  • 表单的提交方式是post
  • 表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”
<form action="/upload" method="post" enctype="multipart/form-data">
    名称<input type="text" name="username"><br/>
    文件1<input type="file" name="uploadFile"><br/>
    <input type="submit" value="提交">
</form>

文件上传的原理

单文件上传的代码实现

第一步:导入相关依赖

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

第二步:在spring-mvc.xml中配置多媒体解析器

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

第三步:完成文件上传

注意,uploadFile这个参数名应该和前端HTML页面中的input标签的name属性值一致。

    @RequestMapping("/upload")
    @ResponseBody
    public void upload(String username, MultipartFile uploadFile) throws IOException {
        System.out.println(username);
        System.out.println(uploadFile);
        // 活得上传文件的名称
        String originalFilename = uploadFile.getOriginalFilename();
        uploadFile.transferTo(new File("C:\\upload\\" + originalFilename));//保存文件
    }

第四步:测试

浏览器页面:

控制台打印:

磁盘本地:

多文件上传的代码实现

多文件上传,只需要将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[ ]数组即可 。

<h1>多文件上传</h1>
<form action="/upload2" method="post" enctype="multipart/form-data">
    名称<input type="text" name="username"><br/>
    文件1<input type="file" name="uploadFile"><br/>
    文件2<input type="file" name="uploadFile"><br/>
    <input type="submit" value="提交">
</form>
    @RequestMapping("/upload2")
    @ResponseBody
    public void upload2(String username,MultipartFile[] uploadFile) throws IOException {
        System.out.println(username);
        for (MultipartFile multipartFile : uploadFile) {
            // 获取文件名
            String originalFilename = multipartFile.getOriginalFilename();
            // 保存到磁盘中通过transferTo(File)方法
            multipartFile.transferTo(new File("C:\\upload\\"+originalFilename));
        }
    }

SpringMVC的拦截器

拦截器的作用

Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。

将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(InterceptorChain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

拦截器也是AOP思想的具体实现。

interceptor和filter区别

关于interceptor和filter的区别,如图所示:

快速入门

自定义拦截器很简单,只有如下三步:

  • ①创建拦截器类实现HandlerInterceptor接口
  • ②配置拦截器
  • ③测试拦截器的拦截效果

第一步:创建拦截器类实现HandlerInterceptor接口

public class MyInterceptor implements HandlerInterceptor {
    //在目标方法执行之前 执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("preHandle.....");
        return true;
    }

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

    //在流程都执行完毕后 执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("afterCompletion....");
    }
}

第二步:在spring-mvc.xml配置文件中配置

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--对哪些资源执行拦截操作-->
            <mvc:mapping path="/**"/>
            <!--配置哪些资源排除拦截操作-->
            <mvc:exclude-mapping path="/user/login"/>
            <bean class="interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

第三步:测试拦截器的拦截效果

    @RequestMapping("test")
    public ModelAndView test(ModelAndView modelAndView){
        System.out.println("目标资源执行......");
        modelAndView.addObject("username","张三");
        modelAndView.setViewName("index.jsp");
        return modelAndView;
    }

浏览器展示:

控制台打印:

快速入门详解

拦截器在预处理后什么情况下会执行目标资源,什么情况下不执行目标资源,以及在有多个拦截器的情况下拦截器的执行顺序是什么?

再编写一个拦截器2

public class MyInterceptor2 implements HandlerInterceptor {
    //在目标方法执行之前 执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("preHandle22222.....");
        return true;
    }

    //在目标方法执行之后 视图对象返回之前执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("postHandle2222...");
    }

    //在流程都执行完毕后 执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("afterCompletion2222....");
    }
}

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

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--对哪些资源执行拦截操作-->
            <mvc:mapping path="/**"/>
            <bean class="interceptor.MyInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!--对哪些资源执行拦截操作-->
            <mvc:mapping path="/**"/>
            <bean class="interceptor.MyInterceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>

结论:

  • 当拦截器的preHandle方法返回true则会执行目标资源,如果返回false则不执行目标资源。
  • 多个拦截器情况下,配置在前的先执行,配置在后的后执行。
  • 拦截器中的方法执行顺序是:preHandler-------目标资源----postHandle---- afterCompletion。

拦截器方法说明

拦截器中的方法说明如下 :

SpringMVC异常处理机制

异常处理的思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。

系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下图:

异常处理两种方式

① 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver

② 实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器

简单异常处理器SimpleMappingExceptionResolver

SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置 :

    <!--配置简单映射异常处理器-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--默认错误视图-->
        <property name="defaultErrorView" value="exception.jsp"/>
        <!--错误视图-->
        <property name="exceptionMappings">
            <!--异常类型-->
            <map>
                <!--其中value值表示的是exception.jsp视图-->
                <entry key="java.lang.IndexOutOfBoundsException" value="exception.jsp"/>
                <entry key="java.lang.ClassCastException" value="exception.jsp"/>
            </map>
        </property>
    </bean>

其中exception.jsp内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Exception</title>
</head>
<body>
<h1>异常</h1>
</body>
</html>

自定义异常处理步骤

步骤总结:

  • ①创建异常处理器类实现HandlerExceptionResolver
  • ②配置异常处理器
  • ③编写异常页面
  • ④测试异常跳转

第一步:创建异常处理器类实现HandlerExceptionResolver

public class MyExceptionResolver implements HandlerExceptionResolver {
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception exception) {
        //处理异常的代码实现
        //创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        // 根据不同错误转向不同页面统一处理,即异常与view的相应关系
        if (exception instanceof IndexOutOfBoundsException) {
            modelAndView.setViewName("exception.jsp");// 指定错误页面的视图名称
            modelAndView.addObject("errorMsg", "索引越界异常IndexOutOfBoundsException");// 还可以向视图页面传递数据
        } else if (exception instanceof NullPointerException) {
            modelAndView.setViewName("exception.jsp");// 指定错误页面的视图名称
            modelAndView.addObject("errorMsg", "空指针异常NullPointerException");
        } else if (exception instanceof NumberFormatException) {
            modelAndView.setViewName("exception.jsp");// 指定错误页面的视图名称
            modelAndView.addObject("errorMsg", "数字格式异常NumberFormatException");
        }
        return modelAndView;
    }
}

第二步:配置异常处理器,将该类声明为Bean组件

<!--配置异常处理器-->
<bean id="exceptionResolver" class="exception.MyExceptionResolver"/>

第三步:编写异常页面exception.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Exception</title>
</head>
<body>
<h1>${errorMsg}</h1>
</body>
</html>

第四步:测试异常跳转

@Controller
public class TestController {
    @RequestMapping("/test1")
    public void test1() {
        Integer[] nums = new Integer[10];
        System.out.println(nums[12]);// 人为制造索引越界异常
    }

    @RequestMapping("/test2")
    public void test2() {
        String str = null;
        str.length();// 人为制造空指针异常
    }

    @RequestMapping("/test3")
    public void test3() {
        Integer.parseInt("abc", 10);// 人为制造数字格式异常
    }
}

第五步:测试结果

本篇文章源码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值