什么是SpringMVC

SpringMVC是Spring的一个后续产品,是Spring的一个子项目

SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案

典型MVC: 模型(dao+service)、视图(jsp)、控制器(Controller)

springmvc模式:

01.jpg

开始

创建maven(web)工程

  • 创建一个模块

  • 在模块pom.xml修改打包方式

    1
    2
    <!--web工程需要打包为.war包-->
    <packaging>war</packaging>
  • 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <dependencies>
    <!-- SpringMVC -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.1</version>
    </dependency>

    <!-- 日志 -->
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
    </dependency>

    <!-- ServletAPI -->
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
    </dependency>

    <!-- Spring5和Thymeleaf整合包 -->
    <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.12.RELEASE</version>
    </dependency>
    </dependencies>
  • 创建一个webapp文件夹,且添加web.xml

01.jpg

  • 配置web.xml,注册SpringMVC的前端控制器DispatcherServlet

    • 默认配置方式
    • 此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <!--
    设置springMVC的核心控制器所能处理的请求的请求路径
    /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
    但是/不能匹配.jsp请求路径的请求
    -->
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  • 扩展配置方式(推荐)

    可通过init-param标签设置SpringMVC配置文件的位置和名称,通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet的初始化时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources -->
    <param-value>classpath:springMVC.xml</param-value>
    </init-param>
    <!--
    作为框架的核心组件,在启动过程中有大量的初始化操作要做
    而这些操作放在第一次请求时才执行会严重影响访问速度
    因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
    -->
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <!--
    设置springMVC的核心控制器所能处理的请求的请求路径
    /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
    但是/不能匹配.jsp请求路径的请求
    -->
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    </web-app>

注:

标签中使用/和/*的区别:

/所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求

因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面

/*则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*的写法

  • 在resources文件夹下创建springMVC.xml文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    <?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包 -->
    <context:component-scan base-package="cn.xlh.controller"></context:component-scan>

    <!-- 配置Thymeleaf视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
    <property name="order" value="1"/>
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine">
    <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
    <property name="templateResolver">
    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">

    <!-- 视图前缀 -->
    <property name="prefix" value="/WEB-INF/templates/"/>

    <!-- 视图后缀 -->
    <property name="suffix" value=".html"/>
    <property name="templateMode" value="HTML5"/>
    <property name="characterEncoding" value="UTF-8"/>
    </bean>
    </property>
    </bean>
    </property>
    </bean>

    <!--
    处理静态资源,例如html、js、css、jpg
    若只设置该标签,则只能访问静态资源,其他请求则无法访问
    此时必须设置<mvc:annotation-driven/>解决问题
    -->
    <mvc:default-servlet-handler/>
    <!-- 开启mvc注解驱动 -->
    <mvc:annotation-driven>
    <mvc:message-converters>
    <!-- 处理响应中文内容乱码 -->
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
    <property name="defaultCharset" value="UTF-8"/>
    <property name="supportedMediaTypes">
    <list>
    <value>text/html</value>
    <value>application/json</value>
    </list>
    </property>
    </bean>
    </mvc:message-converters>
    </mvc:annotation-driven>
    </beans>
  • 新建两个html文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!DOCTYPE html>
    <!--必须包含这个thymeleaf地址,用于文件路径跳转-->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>index</title>
    </head>
    <body>
    <h1>首页</h1>
    <!-- thymeleaf方式 -->
    <a th:href="@{/hello}">HelloWorld</a><br/>
    </body>
    </html>

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>hello</title>
    </head>
    <body>
    <h1>Hello!</h1>
    </body>
    </html>
  • 映射配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package cn.xlh.controller;

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

    @Controller
    public class HelloController {

    @RequestMapping(value = "/")
    public String index() {
    // 返回视图名称,即打开index.html文件
    return "index";
    }

    @RequestMapping(value = "/hello")
    public String hello() {
    // 返回视图名称,即打开hello.html文件
    return "hello";
    }

    }
  • 运行即可

  • 总结:浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面。

  • 项目结构

01.jpg

@RequestMapping注解

功能

从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。

SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

位置

  • 标识一个类:设置映射请求的请求路径的初始信息
  • 标识一个方法:设置映射请求请求路径的具体信息
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/controller01")
public class Controller01 {
@RequestMapping("/test01")
public String test01() {
//访问路径为/study02/controller01/test01
return "test01";
}
}

value属性

  • value属性通过请求的请求地址匹配请求映射

  • value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求

  • value属性必须设置,至少通过请求地址匹配请求映射

    1
    2
    3
    4
    @RequestMapping(
    value = {"/testRequestMapping", "/test"}
    )
    // 表示这两个请求,对应同一个映射

method属性

  • method属性通过请求的请求方式(get或post)匹配请求映射
  • method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
  • 若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method ‘POST’ not supported
1
2
3
4
5
6
7
8
9
//只能访问controller01/test01且请求方式为get
@RequestMapping(
value = {"/test01"},
method = {RequestMethod.GET}
)
public String test01() {
//访问路径为/study02/controller01/test01
return "test01";
}

1、对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解

处理get请求的映射–>@GetMapping

处理post请求的映射–>@PostMapping

处理put请求的映射–>@PutMapping

处理delete请求的映射–>@DeleteMapping

2、常用的请求方式有get,post,put,delete

但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理

params属性(了解)

  • params属性通过请求的请求参数匹配请求映射

  • params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系

    • “param”:要求请求映射所匹配的请求必须携带param请求参数
    • “!param”:要求请求映射所匹配的请求必须不能携带param请求参数
    • “param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value
    • “param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
    1
    2
    <a th:href="@{/test(username='admin',password=123456)">测试@RequestMapping的params属性-->/test</a><br>
    <!-- 这种方式传参不会报错,而使用?可能会报错(但不影响程序) -->

若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面会报错400

headers属性(了解)

  • headers属性通过请求的请求头信息匹配请求映射
  • headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
    • “header”:要求请求映射所匹配的请求必须携带header请求头信息
    • “!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
    • “header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value
    • “header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value

若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到

SpringMVC支持ant风格的路径

  • ?:表示任意的单个字符

    1
    2
    @RequestMapping("/a?a/controller01")
    // 可匹配/aaa/controller、/a1a/contrpller
  • *:表示任意的0个或多个字符

  • **:表示任意的一层或多层目录,只能使用/**/xxx的方式,否则会按照单个*进行处理

SpringMVC支持路径中的占位符(重点)

  • 原始方式:/deleteUser?id=1

  • rest方式:/deleteUser/1

  • SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参

1
<a th:href="@{/controller01/test02/1}">测试rect方式传参</a>
1
2
3
4
5
6
7
//使用占位符表示参数
@RequestMapping("/test02/{id}")
//使用注解将参数作为函数形参
public String test02(@PathVariable("id") Integer id) {
System.out.println("id = " + id);
return "success";
}

SpringMVC获取请求参数

通过ServletAPI获取

将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象。

1
2
3
4
5
6
7
@RequestMapping("/servletAPI")
public String servletAPI(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username + " " + password);
return "success";
}

通过控制器方法的形参获取

1
2
3
4
5
@RequestMapping("/params")
public String servletAPI(String username,String password) {
System.out.println(username + " " + password);
return "success";
}

注:若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数

若使用字符串数组类型的形参,此参数的数组中包含了每一个数据

若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果

@RequestParam

  • @RequestParam是将请求参数和控制器方法的形参创建映射关系

  • @RequestParam注解一共有三个属性:

    • value:指定为形参赋值的请求参数的参数名
    • required:设置是否必须传输此请求参数,默认值为true
      • 若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
      • defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为””时,则使用默认值为形参赋值

@RequestHeader

  • @RequestHeader是将请求头信息和控制器方法的形参创建映射关系

  • @RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

@CookieValue

  • @CookieValue是将cookie数据和控制器方法的形参创建映射关系

  • @CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

通过POJO获取请求参数

可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值。

1
2
3
4
5
6
7
8
<form th:action="@{/testpojo}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男">男<input type="radio" name="sex" value="女">女<br>
年龄:<input type="text" name="age"><br>
邮箱:<input type="text" name="email"><br>
<input type="submit">
</form>
1
2
3
4
5
6
@RequestMapping("/testpojo")
public String testPOJO(User user){
System.out.println(user);
return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='123@qq.com'}

解决获取请求参数的乱码问题

解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--配置springMVC的编码过滤器-->
<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>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效

域对象共享数据

使用ServletAPI向request域对象共享数据

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
<h1>Success</h1>
<p th:text="${name}"></p>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.xlh.controller;

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

import javax.servlet.http.HttpServletRequest;

@Controller
public class ScopeController {
@RequestMapping("/testRequestByServletAPI")
public String testRequestByServletAPI(HttpServletRequest request) {
request.setAttribute("name", "肖林航");
return "success";
}
}

注:html使用thy的th:text报错可以在设置中关闭:Editor->Inspections->搜索thy->关闭Expression勾选

使用Model向request域对象共享数据

1
2
3
4
5
@RequestMapping("/testModel")
public String testModel(Model model) {
model.addAttribute("name", "肖林航");
return "success";
}

使用ModelMap向request域对象共享数据

1
2
3
4
5
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap map) {
map.addAttribute("name","肖林航");
return "success";
}

Model、ModelMap、Map的关系

Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的.

向session域共享数据

1
2
3
4
5
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
1
<p th:text="${session.testSessionScope}"></p>

向application域共享数据

1
2
3
4
5
6
@RequestMapping("/testApplication")
public String testApplication(HttpSession session) {
ServletContext sc = session.getServletContext();
sc.setAttribute("name", "xlh");
return "success";
}

SpringMVC的视图

  • SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户

  • SpringMVC视图的种类很多,默认有转发视图和重定向视图

ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。

转发视图

SpringMVC中默认的转发视图是InternalResourceView

SpringMVC中创建转发视图的情况:

  • 当控制器方法中所设置的视图名称以”forward:”为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”forward:”去掉,剩余部分作为最终路径通过转发的方式实现跳转
1
2
3
4
5
6
7
8
9
10
11
12
//被转发到的
@RequestMapping("/forwarded")
public String forwarded() {
return "success";
}

//转发
@RequestMapping("/testForward")
public String testForward(HttpSession session) {
session.setAttribute("testSessionScope", "hello,session");
return "forward:/forwarded";
}

重定向视图

SpringMVC中默认的重定向视图是RedirectView

当控制器方法中所设置的视图名称以”redirect:”为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”redirect:”去掉,剩余部分作为最终路径通过重定向的方式实现跳转

1
2
3
4
5
6
7
//重定向
@RequestMapping("/testRedirect")
public String testRedirect(HttpSession session) {
session.setAttribute("testSessionScope", "hello,session");
return "redirect:/forwarded";
}
// 重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径

视图控制器view-controller

当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示.

1
2
3
4
5
6
7
8
9
10
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称

//@RequestMapping("/")
//public String index() {
// return "index";
//}二者等价
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>

注:当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:<mvc:annotation-driven />

RESTful

由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求

HiddenHttpMethodFilter*处理put和delete请求的条件:

  • 当前请求的请求方式必须为post
  • 当前请求必须传输请求参数_method

在web.xml中注册HiddenHttpMethodFilter

1
2
3
4
5
6
7
8
<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中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

使用

  • 控制器方法

    1
    2
    3
    4
    5
    @RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
    public String deleteEmployee(@PathVariable("id") Integer id){
    employeeDao.delete(id);
    return "redirect:/employee";
    }
  • 表单

    1
    2
    3
    4
    5
    <!-- 作用:通过超链接控制表单的提交,将post请求转换为delete请求 -->
    <form id="delete_form" method="post">
    <!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
    <input type="hidden" name="_method" value="delete"/>
    </form>

HttpMessageConverter

HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文。

@RequestBody

@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。

1
2
3
4
5
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
1
2
3
4
public String testRequestBody(@RequestBody String requestbody) {
System.out.println(requestbody);
return "success";
}
1
输出:username=121&password=1241

@RequestEntity

RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息。

1
2
3
4
5
6
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
System.out.println("requestHeader:"+requestEntity.getHeaders());
System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
1
2
3
输出结果:
requestHeader:[host:"localhost:8080", connection:"keep-alive", content-length:"27", cache-control:"max-age=0", sec-ch-ua:"" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"", sec-ch-ua-mobile:"?0", upgrade-insecure-requests:"1", origin:"http://localhost:8080", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"]
requestBody:username=admin&password=123

@ResponseBody

@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器.

1
2
3
4
5
6
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success1";
}
// 页面将显示success1

SpringMVC处理json

  • 导入jackson的依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
    </dependency>
  • 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串

    1
    <mvc:annotation-driven />
  • 处理器方法上使用@ResponseBody注解进行标识

  • 将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串

    1
    2
    3
    4
    5
    6
    7
    @RequestMapping("/testResponseUser")
    @ResponseBody
    public User testResponseUser(){
    return new User(1001,"admin","123456",23,"男");
    }

    // 结果:{"id":1001,"username":"admin","password":"123456","age":23,"sex":"男"}

SpringMVC处理ajax

  • 请求超链接

    1
    2
    3
    <div id="app">
    <a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
    </div>
  • 通过vue和axios处理点击事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
    <script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
    <script type="text/javascript">
    var vue = new Vue({
    el:"#app",
    methods:{
    testAjax:function (event) {
    axios({
    method:"post",
    url:event.target.href,
    params:{
    username:"admin",
    password:"123456"
    }
    }).then(function (response) {
    alert(response.data);
    });
    // 取消超链接的默认行为
    event.preventDefault();
    }
    }
    });
    </script>
  • 控制器方法

    1
    2
    3
    4
    5
    6
    @RequestMapping("/testAjax")
    @ResponseBody
    public String testAjax(String username, String password){
    System.out.println("username:"+username+",password:"+password);
    return "hello,ajax";
    }

@RestController注解

@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解。

ResponseEntity

ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文。

文件上传和下载

文件上传

  • 文件上传要求form表单的请求方式必须为post,并且添加属性enctype=”multipart/form-data”

    1
    2
    3
    4
    <form action="....." method='post' enctype="multipart/form-data">
    <input type='file' name='photo'>
    <input type='submit' value='上传'>
    </form>
  • 添加依赖

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
    </dependency>
  • 在SpringMVC的配置文件中添加配置

    1
    2
    3
    <!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
    <bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
  • 控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @RequestMapping("/testUp")
    public String testUp(MultipartFile photo, HttpSession session) throws IOException {
    //获取上传的文件的文件名
    String fileName = photo.getOriginalFilename();
    //处理文件重名问题
    String hzName = fileName.substring(fileName.lastIndexOf("."));
    fileName = UUID.randomUUID().toString() + hzName;
    //获取服务器中photo目录的路径
    ServletContext servletContext = session.getServletContext();
    String photoPath = servletContext.getRealPath("photo");
    File file = new File(photoPath);
    if(!file.exists()){
    file.mkdir();
    }
    String finalPath = photoPath + File.separator + fileName;
    //实现上传功能
    photo.transferTo(new File(finalPath));
    return "success";
    }

文件下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}

拦截器

配置

  • SpringMVC中的拦截器需要实现HandlerInterceptor接口
1
2
3
4
5
6
7
8
9
10
11
12
13
<mvc:interceptors>
<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/testRequestEntity"/>
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!--
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
</mvc:interceptors>

拦截器的三个抽象方法

SpringMVC中的拦截器有三个抽象方法:

  • preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
  • postHandle:控制器方法执行之后执行postHandle()
  • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

多个拦截器的执行顺序

  • 若每个拦截器的preHandle()都返回true,此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
    • preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
  • 若某个拦截器的preHandle()返回了false
    • preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

异常处理器

基于配置的异常处理

SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver

SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>

基于注解的异常处理

1
2
3
4
5
6
7
8
9
10
11
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
@ExceptionHandler(ArithmeticException.class)
//ex表示当前请求处理中出现的异常对象
public String handleArithmeticException(Exception ex, Model model){
model.addAttribute("ex", ex);
return "error";
}
}

注解配置SpringMVC

使用配置类和注解代替web.xml和SpringMVC配置文件的功能

  • 创建初始化类,代替web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    /**
    * 指定spring的配置类
    * @return
    */
    @Override
    protected Class<?>[] getRootConfigClasses() {
    return new Class[]{SpringConfig.class};
    }

    /**
    * 指定SpringMVC的配置类
    * @return
    */
    @Override
    protected Class<?>[] getServletConfigClasses() {
    return new Class[]{WebConfig.class};
    }

    /**
    * 指定DispatcherServlet的映射规则,即url-pattern
    * @return
    */
    @Override
    protected String[] getServletMappings() {
    return new String[]{"/"};
    }

    /**
    * 添加过滤器
    * @return
    */
    @Override
    protected Filter[] getServletFilters() {
    CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
    encodingFilter.setEncoding("UTF-8");
    encodingFilter.setForceRequestEncoding(true);
    HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
    return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
    }
    }
  • 创建SpringConfig配置类,代替spring的配置文件

    1
    2
    3
    4
    @Configuration
    public class SpringConfig {
    //ssm整合之后,spring的配置信息写在此类中
    }
  • 创建WebConfig配置类,代替SpringMVC的配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    @Configuration
    //扫描组件
    @ComponentScan("com.atguigu.mvc.controller")
    //开启MVC注解驱动
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {

    //使用默认的servlet处理静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
    }

    //配置文件上传解析器
    @Bean
    public CommonsMultipartResolver multipartResolver(){
    return new CommonsMultipartResolver();
    }

    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    FirstInterceptor firstInterceptor = new FirstInterceptor();
    registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
    }

    //配置视图控制

    /*@Override
    public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/").setViewName("index");
    }*/

    //配置异常映射
    /*@Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
    Properties prop = new Properties();
    prop.setProperty("java.lang.ArithmeticException", "error");
    //设置异常映射
    exceptionResolver.setExceptionMappings(prop);
    //设置共享异常信息的键
    exceptionResolver.setExceptionAttribute("ex");
    resolvers.add(exceptionResolver);
    }*/

    //配置生成模板解析器
    @Bean
    public ITemplateResolver templateResolver() {
    WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
    // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
    ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
    webApplicationContext.getServletContext());
    templateResolver.setPrefix("/WEB-INF/templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setCharacterEncoding("UTF-8");
    templateResolver.setTemplateMode(TemplateMode.HTML);
    return templateResolver;
    }

    //生成模板引擎并为模板引擎注入模板解析器
    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    return templateEngine;
    }

    //生成视图解析器并未解析器注入模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setCharacterEncoding("UTF-8");
    viewResolver.setTemplateEngine(templateEngine);
    return viewResolver;
    }
    }