一文讲明SpringMVC 【爆肝整理一万五千字】

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 文章提供了一份全面的SpringMVC教程,涵盖了SpringMVC的简介、创建实例、注解、获取请求参数的不同方式、域对象共享数据、视图渲染、RESTful风格、拦截器使用、异常处理以及文件上传下载等内容,并包含了相应的代码示例和测试结果。

案例代码:Github 传送门
可参考相关源码

在这里插入图片描述

1、SpringMVC简介

1.1、什么是MVC

MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分

M:Model,模型层,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

● 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
● 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。

V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器。

MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller,调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

1.2、什么是SpringMVC

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

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

注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

1.3、SpringMVC的特点

● 1、Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
● 2、基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
● 3、表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
● 4、代码清新简洁,大幅度提升开发效率
● 5、内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
● 6、性能卓著,尤其适合现代大型、超大型互联网项目要求

2、SpringMVC创建实例Hello SpringMVC(maven+tomcat)

2.1、开发环境

● IDE:idea 2020
● 构建工具:maven3.3.9
● 服务器:tomcat 8
● Spring版本:5.3.1

2.2、创建maven工程(加入pom依赖)

如何在idea中集成maven、之前写过。这里不在赘述
2022最新版超详细的Maven下载配置教程、IDEA中集成maven(包含图解过程)、以及导入项目时jar包下载不成功的问题解决

● 添加web模块
● 打包方式:war
● 引入依赖

<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>compile</scope>
    </dependency>
    <!-- Spring5和Thymeleaf整合包 -->
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring5</artifactId>
        <version>3.0.12.RELEASE</version>
    </dependency>
</dependencies>

注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。

2.3、配置web.xml

2.3.1 生成web.xml

过程:创建webapp包、然后点击项目结构、找到对应的模块、添加web.xml。(对应的web.xml路径要写对)。然后自动在项目中完成创建

注册SpringMVC的前端控制器DispatcherServlet

2.3.2 默认配置方式

此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml

springMVC org.springframework.web.servlet.DispatcherServlet springMVC /

2.3.3 扩展配置方式

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

注意:这个是WEB-INF下的web.xml

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
    <init-param>
        <!-- contextConfigLocation为固定值 -->
        <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>

提示、默认扩展,就是将外部的MVC文件引入,所以需要创建外部的xml文件

通过以下方式进行创建xml文件

注:标签中使用/和/*的区别:
/所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求,因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面

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

2.4、创建请求控制器

由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器
请求控制器中每一个处理请求的方法成为控制器方法
因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识
为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在

/**
 * @author Lenovo
 * @version 1.0
 * @data 2022/10/30 16:41
 */
@Controller
public class HelloSpringMVC {

}

2.5、创建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.zyz.mvc.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>

</beans>

2.6、配置tomcat

具体过程,请参考:2022年最新最详细在IDEA中配置Tomcat(含有详细图解过程)、建立使用IEDA建立一个Web项目的案例

2.7、测试HelloSpringMVC

2.7.1 HelloSpringMVC.java

/**
 * @author Lenovo
 * @version 1.0
 * @data 2022/10/30 16:41
 */
@Controller
public class HelloSpringMVC {

/**
 * @RequestMapping注解:处理请求和控制器方法之间的映射关系
 * @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
 * localhost:8080/springMVC/ 
 */
    @RequestMapping(value = "/")
    public String index(){
        //返回视图名称
        return "index";
    }


}

2.7.2 index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>HELLO SpringMVC</h1>

<a th:href="@{/target}">访问目标页面target.html</a>
</body>
</html>

2.7.3 测试结果

2.8、过超链接跳转到指定页面

2.8.1 index.html

同7.2 index.html

注意:要首先引入http://www.thymeleaf.org

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>HELLO SpringMVC</h1>

<a th:href="@{/target}">访问目标页面target.html</a>
</body>
</html>

2.8.2 target.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>目标页面</title>
</head>
<body>

<p style="font-size: 48px;color: red">这里是TARGET页面</p>

</body>
</html>

2.8.3 控制类

/**
 * @author Lenovo
 * @version 1.0
 * @data 2022/10/30 16:41
 */
@Controller
public class HelloSpringMVC {

    @RequestMapping(value = "/target")
    public String toTarget(){
        //返回目标页面
        return "target";
    }

}

2.8.4 测试结果

2.9、项目结构

2.9.1 HelloSpringMVC.java

package com.zyz.mvc.controller;

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


/**
 * @author Lenovo
 * @version 1.0
 * @data 2022/10/30 16:41
 */
@Controller
public class HelloSpringMVC {
/**
 * @RequestMapping注解:处理请求和控制器方法之间的映射关系
 * @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
 * localhost:8080/springMVC/
 */
    @RequestMapping(value = "/")
    public String index(){
        //返回视图名称
        return "index";
    }

    @RequestMapping(value = "/target")
    public String toTarget(){
        //返回目标页面
        return "target";
    }

}

2.9.2 springMVC.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 http://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描组件-->
    <context:component-scan base-package="com.zyz.mvc.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>

</beans>

2.9.3 index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>HELLO SpringMVC</h1>

<a th:href="@{/target}">访问目标页面target.html</a>
</body>
</html>

2.9.4 target.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>目标页面</title>
</head>
<body>

<p style="font-size: 48px;color: red">这里是TARGET页面</p>

</body>
</html>

2.9.5 web.xml

<?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>
            <!-- contextConfigLocation为固定值 -->
            <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>

2.10 、如何让创建的模板包含xmlns:th=“http://www.thymeleaf.org”

3、@RequestMapping注解

3.1、@RequestMapping注解的功能

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

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

3.2、@RequestMapping注解的位置

● @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
● @RequestMapping标识一个方法:设置映射请求请求路径的具体信息

3.2.1 作用在类上实例

比如A类和B类中都有一个list。为了区分是:在类上加上注解、那么访问的地址就变成/A/list。/B/list。很实用的一个功能

@Controller
@RequestMapping("/hello")
public class TestController {

    /**
     * 此时请求的路径是/hello/test
     * @return
     */
    @RequestMapping("/test")
    public String success(){
        return "success";
    }
}

访问方式

<a th:href="@{/hello/test}">访问目标页面target.html</a>

测试结果

3.2.2 仅作用在方法上

直接通过方法上的路径就可以访问到,

@Controller
public class TestController {

    /**
     * 此时请求的路径是test
     * @return
     */
    @RequestMapping("/test")
    public String success(){
        return "success";
    }
}

访问方式

<a th:href="@{/test}">访问目标页面target.html</a>

测试结果

3.3、@RequestMapping注解的value属性

● @RequestMapping注解的value属性通过请求的请求地址匹配请求映射
● @RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
● @RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射

注:value一般写一个匹配路径就行

3.3.1 测试

@Controller
public class TestController {
    @RequestMapping(value = {"/test","/myTest"})
    public String success(){
        return "success";
    }
}
<a th:href="@{/test}">访问目标页面target.html</a>

<a th:href="@{/myTest}">访问目标页面target.html</a>

3.4、@RequestMapping注解的method属性

● @RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
● @RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
● 若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method ‘POST’ not supported

注:restful风格经常使用method和value联合使用

3.4.1、同时满足value和method

<form th:action="@{/test}" method="post">
    <input type="submit">
</form>

@Controller
public class TestController {
    @RequestMapping(
            value = {"/test", "/myTest"},
            method = {RequestMethod.GET, RequestMethod.POST}
    )
    public String success() {
        return "success";
    }
}

3.4.2 满足value、不满足method的情况

<form th:action="@{/test}" method="post">
    <input type="submit">
</form>

@Controller
public class TestController {
    @RequestMapping(value = {"/test","/myTest"},method = RequestMethod.GET)
    public String success(){
        return "success";
    }
}

1、对于处理指定请求方式的控制器方法,SpringMVC中提供@RequestMapping的派生注解
处理get请求的映射–>@GetMapping
处理post请求的映射–>@PostMapping
处理put请求的映射–>@PutMapping
处理delete请求的映射–>@DeleteMapping
2、常用的请求方式有get,post,put,delete
但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符
串(put或delete),则按照默认的请求方式get处理

还是建议使用restful风格

3.5、@RequestMapping注解的params属性

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

1、“param”:要求请求映射所匹配的请求必须携带param请求参数 “2、!param”:要求请求映射所匹配的请求必须不能携带param请求参数
3、 “param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value
4、“param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value

注:用的较多也是携带参数

3.5.1 使用数组的形式

<form th:action="@{/test(username='张三',password='1234')}" method="post">
    <input type="submit">
</form>

@Controller
public class TestController {
    @RequestMapping(
            value = {"/test", "/myTest"},
            method = {RequestMethod.GET, RequestMethod.POST},
            params = {"username","password"}
    )
    public String success() {
        return "success";
    }
}

3.5.2 不适用数组的形式

<form th:action="@{/test(username='张三')}" method="post">
    <input type="submit">
</form>

@Controller
public class TestController {
    @RequestMapping(
            value = {"/test", "/myTest"},
            method = {RequestMethod.GET, RequestMethod.POST},
            params = "username"
    )
    public String success() {
        return "success";
    }
}

3.5.3 参数不等于某个数的情况

<form th:action="@{/test(username='张三',password='1234')}" method="post">
    <input type="submit">
</form>

@Controller
public class TestController {
    @RequestMapping(
            value = {"/test", "/myTest"},
            method = {RequestMethod.GET, RequestMethod.POST},
            params = {"username","password!=1234"}
    )
    public String success() {
        return "success";
    }
}

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

提示:这种情况目前我是没遇到过这样的写法

3.6、@RequestMapping注解的headers属性

● @RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
● @RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系

“header”:要求请求映射所匹配的请求必须携带header请求头信息
“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value
“header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value

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

了解就行

3.6.1 测试

请求头中需要携带的信息

<form th:action="@{/test(username='张三',password='12345')}" method="post">
    <input type="submit">
</form>

@Controller
public class TestController {
    @RequestMapping(
            value = {"/test", "/myTest"},
            method = {RequestMethod.GET, RequestMethod.POST},
            params = {"username","password!=1234"},
            headers = {"Host=localhost:8088"}
    )
    public String success() {
        return "success";
    }
}

3.7、SpringMVC支持ant风格的路径

?:表示任意的单个字符
*:表示任意的0个或多个字符
:表示任意的一层或多层目录
注意:在使用
时,只能使用/**/xxx的方式

3.7.1 测试单个字符

<a th:href="@{/fgf/mytest}">访问目标页面target.html</a>
@Controller
public class TestController {
    @RequestMapping("/f?f/mytest")
    public String myTest() {
        return "success";
    }
    }

3.7.2测试多个字符

<a th:href="@{/fg54edrf/mytest}">访问目标页面target.html</a>
@Controller
public class TestController {
    @RequestMapping("/f*f/mytest")
    public String myTest() {
        return "success";
    }
    }

3.7.3 测试目录结构

多级目录都可以访问到、一般也不这样用吧

<a th:href="@{/fg54edrf/mytest}">访问目标页面target.html</a>
@Controller
public class TestController {
    @RequestMapping("/**/mytest")
    public String myTest() {
        return "success";
    }
    }

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

SpringMVC路径中的占位符常用于RESTful风格中、我就喜欢这样使用

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

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

3.8.1 测试

<a th:href="@{/mytest/4/张三}">访问目标页面target.html</a>
@Controller
public class TestController {
    @RequestMapping("/mytest/{id}/{username}")
    public String myTest(@PathVariable("id") String id,@PathVariable("username") String username) {
        System.out.println("id:"+ id+",username:"+username);
        return "success";
    }

3.8.2 项目中的实际引用

后端接口设计

4、SpringMVC获取请求参数(六种方式详细说明)

4.1、通过ServletAPI获取

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

4.1.1 测试

<a th:href="@{/testServletAPI(username='张三',password='123456')}">测试</a>

@Controller
public class paramController {

    @RequestMapping("/testServletAPI")
    public String testParam(HttpServletRequest request){
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("username:"+username+",password:"+password);
        return "param_test";

    }
}

4.2、通过控制器方法的形参获取请求参数

在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参

4.2.1 测试

<a th:href="@{/testServletAPI(username='张三',password='123456')}">测试</a>

@Controller
public class paramController {
    @RequestMapping("/testServletAPI")
    public String testParam(String username,String password){
        System.out.println("username:"+username+",password:"+password);
        return "param_test";

    }
}

4.2.2 测试

<form th:action="@{/testParam}" method="post">
    姓名:<input type="text" name="username">
    密码:<input type="password" name="password">
    爱好:<input type="checkbox" name="hobby" value="a">a
    <input type="checkbox" name="hobby" value="b">b
    <input type="checkbox" name="hobby" value="c">c<br>
    <button type="submit" >提交</button>
</form>

@Controller
public class paramController {
    @RequestMapping("/testParam")
    public String testParam(String username,String password,String hobby){
        System.out.println("username:"+username+",password:"+password+",hobby:"+hobby);
        return "param_test";

    }
}

4.2.3 测试

@Controller
public class paramController {

    @RequestMapping("/testParam")
    public String testParam(String username,String password,String[] hobby){
        System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
        return "param_test";

    }
}

注: 若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串 数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果

4.3、@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所指定的请求参数没有传输或传输的值
为""时,则使用默认值为形参赋值

4.3.1 测试1、形参不匹配

4.3.2 测试二、解决方法

@RequestParam("user_name") String username。添加一个注解

<form th:action="@{/testParam}" method="get">
    姓名:<input type="text" name="user_name">
    密码:<input type="password" name="password">
    爱好:<input type="checkbox" name="hobby" value="a">a
    <input type="checkbox" name="hobby" value="b">b
    <input type="checkbox" name="hobby" value="c">c<br>
    <button type="submit" >提交</button>
</form>

@Controller
public class paramController {

    @RequestMapping("/testParam")
    public String testParam(@RequestParam("user_name") String username, String password, String[] hobby){
        System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
        return "param_test";

    }
}

4.3.3 测试三、参数是否必须传值

默认是必须传值


设置这个参数非必须

    @RequestMapping("/testParam")
    public String testParam(@RequestParam(value = "user_name",required = false) String username, String password, String[] hobby){
        System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
        return "param_test";

    }



默认是必须传值

    @RequestMapping("/testParam")
    public String testParam(@RequestParam("user_name") String username, String password, String[] hobby){
        System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
        return "param_test";
    }

4.3.4 测试四、默认值

@Controller
public class paramController {

    @RequestMapping("/testParam")
    public String testParam(@RequestParam(value = "user_name", required = false,defaultValue = "hello") String username, String password, String[] hobby) {
        System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby));
        return "param_test";

    }
}

4.4、@RequestHeader

● @RequestHeader是将请求头信息和控制器方法的形参创建映射关系
● @RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

4.4.1 测试

@Controller
public class paramController {

    @RequestMapping("/testParam")
    public String testParam(@RequestParam(value = "user_name", required = false,defaultValue = "hello") String username, String password, String[] hobby,
                            @RequestHeader(value = "ShowHost" ,required = true,defaultValue = "MyHost") String host) {
        System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby)+",Host:"+host);
        return "param_test";

    }
}

4.5、@CookieValue

● @CookieValue是将cookie数据和控制器方法的形参创建映射关系
● @CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

4.6、通过POJO获取请求参数

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

4.6.1 测试

<form th:action="@{/myTestParam}" method="get">
    姓名:<input type="text" name="username">
    密码:<input type="password" name="password">
    性别:<input type="text" name="sex">
    <button type="submit" >提交</button>
</form>

@Controller
public class paramController {
    @RequestMapping("/myTestParam")
    public String myTest(User user){
        System.out.println(user);
        return "param_test";
    }
    }

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

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

  <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>

环境搭建:改系列文章开始有介绍。这部分代码所在磁盘位置:F:\workspace\SpringMVC代码\springMVC-demo4

从源码角度分析控制器:https://blog.csdn.net/weixin\_43304253/article/details/128071577

5、域对象共享数据

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

设置:request.setAttribute(“testRequestScope”,“Hello,ServletAPI”);。取值:通过key

5.1.1 核心部分代码

设置值

/**
 * 使用servletAPI向request域对象共享数据
 * @param request
 * @return
 */
@RequestMapping("/testRequestScope")
public String toSuccess(HttpServletRequest request){
    request.setAttribute("testRequestScope","Hello,ServletAPI");
    return "success";
}

跳转后页面获取数据

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>success</title>
</head>
<body>
<h1>这里是成功页面</h1>
<p th:text="${testRequestScope}"></p>
</body>
</html>

5.1.2 测试结果

5.2、使用ModelAndView向request域对象共享数据

5.1.1 核心代码

/**
 * @return
 */
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
    /**
     * ModelAndView有Model和View的功能
     * Model主要用于向请求域共享数据
     * View主要用于设置视图,实现页面跳转
     */
    ModelAndView mav = new ModelAndView();
    //用来处理模型数据,向请求域request共享数据
    mav.addObject("testRequestScope", "Hello,ModelAndView");
    //设置视图名称
    mav.setViewName("success");
    return mav;

}

取值同1.1

5.1.2 测试结果

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

5.3.1 核心代码

/**
 * 测试Model
 * @param model
 * @return
 */
@RequestMapping("/testModel")
public String testModel(Model model){
    model.addAttribute("testRequestScope","Hello,Model");
    return "success";
}

取值同1.1

5.3.2 测试结果

5.4、使用map向request域对象共享数据

5.4.1 核心代码

/**
 * 测试MaP
 * @param map
 * @return
 */
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){
    map.put("testRequestScope","Hello,Map");
    return "success";
}

取值方法同1.1

5.4.2 测试结果

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

5.5.1 核心代码

/**
 * 测试 ModelMap
 * @param modelMap
 * @return
 */
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
    modelMap.addAttribute("testRequestScope","Hello,ModelMap");
    return "success";
}

取值同1.1

5.5.2 测试结果

5.6、Model、ModelMap、Map的关系

CTRL+H 查看实现接口类。通过反射查看调用的对应方法

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

public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}

通过反射查看调用的方法

System.out.println(model.getClass().getName());
System.out.println(map.getClass().getName());
System.out.println(modelMap.getClass().getName());

5.7、向session域共享数据

5.7.1 核心代码

/**
 * 测试Session
 * @param session
 * @return
 */
@RequestMapping("/testSession")
public String testSession(HttpSession session){
    session.setAttribute("testSession","Hello Session");
    return "success";

}

取值方法

<p th:text="${session.testSession}"></p>

5.7.2 测试结果

5.8、向application域共享数据

5.8.1 核心代码

/**
 * 测试Application
 * @param session
 * @return
 */
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
    ServletContext application = session.getServletContext();
    application.setAttribute("testApplication","Hello Application");
    return "success";
}

获取数据

<p th:text="${application.testApplication}"></p>

5.8.2 测试结果

5.9、项目结构

5.9.1 结构

5.9.2 首页跳转链接

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<a th:href="@{/testRequestScope}">测试requestAPI</a>
<hr>
<a th:href="@{/testModelAndView}">测试ModelAndView</a>
<hr>
<a th:href="@{/testModel}">测试Model</a>
<hr>
<a th:href="@{/testMap}">测试Map</a>
<hr>
<a th:href="@{/testModelMap}">测试ModelMap</a>
<hr>
<a th:href="@{/testSession}">测试Session</a>
<hr>
<a th:href="@{/testApplication}">测试Application</a>
</body>
</html>

5.9.3 数据展示页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>success</title>
</head>
<body>
<h1>这里是成功页面</h1>
<p th:text="${testRequestScope}"></p>
<p th:text="${session.testSession}"></p>
<p th:text="${application.testApplication}"></p>
</body>
</html>

5.9.4 跳转控制器

package com.zyz.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.util.Map;

/**
 * @author zyz
 * @version 1.0
 * @data 2022/11/27 20:58
 * @Description:
 */

@Controller
public class TestController {
    @RequestMapping("/")
    public String toIndex() {
        return "index";
    }


    /**
     * 使用servletAPI向request域对象共享数据
     *
     * @param request
     * @return
     */
    @RequestMapping("/testRequestScope")
    public String toSuccess(HttpServletRequest request) {
        request.setAttribute("testRequestScope", "Hello,ServletAPI");
        return "success";
    }

    /**
     * 测试ModelAndView
     * @return
     */
    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        /**
         * ModelAndView有Model和View的功能
         * Model主要用于向请求域共享数据
         * View主要用于设置视图,实现页面跳转
         */
        ModelAndView mav = new ModelAndView();
        //用来处理模型数据,向请求域request共享数据
        mav.addObject("testRequestScope", "Hello,ModelAndView");
        //设置视图名称
        mav.setViewName("success");
        return mav;

    }

    /**
     * 测试Model
     * @param model
     * @return
     */
    @RequestMapping("/testModel")
    public String testModel(Model model){
        model.addAttribute("testRequestScope","Hello,Model");
        System.out.println(model.getClass().getName());
        return "success";
    }

    /**
     * 测试MaP
     * @param map
     * @return
     */
    @RequestMapping("/testMap")
    public String testMap(Map<String,Object> map){
        map.put("testRequestScope","Hello,Map");
        System.out.println(map.getClass().getName());
        return "success";
    }

    /**
     * 测试 ModelMap
     * @param modelMap
     * @return
     */
    @RequestMapping("/testModelMap")
    public String testModelMap(ModelMap modelMap){
        modelMap.addAttribute("testRequestScope","Hello,ModelMap");
        System.out.println(modelMap.getClass().getName());
        return "success";
    }

    /**
     * 测试Session
     * @param session
     * @return
     */
    @RequestMapping("/testSession")
    public String testSession(HttpSession session){
        session.setAttribute("testSession","Hello Session");
        return "success";

    }

    /**
     * 测试Application
     * @param session
     * @return
     */
    @RequestMapping("/testApplication")
    public String testApplication(HttpSession session){
        ServletContext application = session.getServletContext();
        application.setAttribute("testApplication","Hello Application");
        return "success";
    }



}

5.10 SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)

1、返回视图打断点

2、刷新页面进入断点
在这个位置打两个断点、进入视图跳转控制器

3、进入processDispatchResult方法

4、进入render方法

5、查看使用的哪种视图

SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
SpringMVC视图的种类很多,默认有转发视图和重定向视图
当工程引入jstl的依赖,转发视图会自动转换为JstlView
若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视
图解析器解析之后所得到的是ThymeleafView

通过Debug模式查看:通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)

6、SpringMVC的视图

6.1、ThymeleafView

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

6.1.1 核心代码

/**
 * 测试testThymeleafView
 * @return
 */
@RequestMapping("/testThymeleafView")
public String testView(){
    return "success";
}

6.1.2 测试结果

6.1.3 通过源码查看

6.2、转发视图

SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况:
当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视
图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:“去掉,剩余部分作为最终路径通过转发的方式实现跳转
例如"forward:/”,“forward:/employee”

6.2.1 核心代码

@RequestMapping(“/testThymeleafView”)
public String testView(){
return “success”;
}

/**

  • 测试forward
  • @return
    */
    @RequestMapping(“/testForward”)
    public String testForward(){
    return “forward:/testThymeleafView”;
    }

6.2.2 测试结果

6.2.3 源码查看

6.3、重定向视图

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

6.3.1 核心代码

@RequestMapping("/testThymeleafView")
public String testView(){
    return "success";
}


/**
 * 测试redirect 重定向
 * @return
 */
@RequestMapping("/testRedirect")
public String testRedirect(){
    return "redirect:/testThymeleafView";
}

6.3.2 测试结果

6.3.3 源码查看

6.4、视图控制器view-controller

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

<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>

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

6.4.1 核心代码

  <mvc:view-controller path="/" view-name="index"></mvc:view-controller>

代替

    @RequestMapping("/")
    public String toIndex() {
        return "index";
    }

需要添加注解驱动、否则跳转连接无效

    <!--开启mvc的注解驱动-->
    <mvc:annotation-driven/>

6.4.2 开启后、正常跳转

6.4.3 如果不开启、跳转失败

7、RESTful的详细介绍和使用 具体代码案例分析

7.1、RESTful简介

REST:Representational State Transfer,表现层资源状态转移。

● a>资源
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。
● b>资源的表述
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
● c>状态转移
状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

7.2、RESTful的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。

7.3、HiddenHttpMethodFilter

由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?

SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求HiddenHttpMethodFilter 处理put和delete请求的条件:

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

满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数
method的值,因此请求参数method的值才是最终的请求方式
在web.xml中注册HiddenHttpMethodFilter

<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>

目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和
HiddenHttpMethodFilter
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter
原因:
在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字
符集的
request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作
而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:String paramValue = request.getParameter(this.methodParam);

7.4、实际代码案例

7.4.1 控制层

package com.zyz.mvc.controller;

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

/**
 * @author zyz
 * @version 1.0
 * @data 2022/11/28 15:17
 * @Description:
 */

@Controller
public class ResultController {
    /**
     *  使用RESULT模拟用户资源的增删改查
     *


     /**
     *查询所有用户信息
     * @return
     */
    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String getAllUser(){
        System.out.println("查询所有用户信息");
        return "success";
    }

    /**
     * 根据ID查询用户信息
     * @return
     */
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String getUserById(){
        System.out.println("根据ID查询用户信息");
        return "success";
    }

    /**
     * 添加用户
     * @param username
     * @param password
     * @return
     */
    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String addUser(String username,String password){
        System.out.println("添加用户信息:"+username+","+password);
        return "success";
    }

    /**
     * 修改用户
     * @param username
     * @param password
     * @return
     */
    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String updateUser(String username,String password){
        System.out.println("修改用户信息:"+username+","+password);
        return "success";
    }




}

7.4.2 页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a th:href="@{/user}">查询所有用户</a>
<br>
<a th:href="@{/user/1}">根据ID查询用户信息</a>
<br>
<form th:action="@{/user}" method="post" >
    用户名:<input type="text" name="username">
    <br>
    密码:<input type="password" name="password">
    <br>
    <input type="submit" value="添加">

</form>

<hr>
<form th:action="@{/user}" method="post" >
    <input type="hidden" name="_method" value="PUT">
    用户名:<input type="text" name="username">
    <br>
    密码:<input type="password" name="password">
    <br>
    <input type="submit" value="添加">

</form>

</body>
</html>

7.4.3 测试结果

8、SpringMVC创建JSP页面的详细过程+配置模板+实现页面跳转+配置Tomcat

8.1、创建Maven项目

8.2、填写项目基本信息、完成创建

8.3、导入POM依赖

打包方式设置为War包

  <packaging>war</packaging>

依赖、可自行添加需要jar包依赖

<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>compile</scope>
        </dependency>
        <!-- Spring5和Thymeleaf整合包 -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

8.4、选择项目结构

8.5、创建web.xml

如果创建的项目无法展开目录、请重启IDEA、或者清缓存

8.6、点击应用apply、查看项目是否创建成功

8.7、配置web.xml

<?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">

    <!--配置过滤器编码-->
    <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的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
        <init-param>
            <!-- contextConfigLocation为固定值 -->
            <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>

8.8、配置模板解析器

springMVC.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 http://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描组件-->
    <context:component-scan base-package="com.zyz.mvc.controller"></context:component-scan>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/templates/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>



</beans>

8.9、配置包templates

8.10、创建index.jsp

创建的index.jsp页面是和WEB-INFP平级

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: Lenovo
  Date: 2022/11/29
  Time: 10:11
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
这是我的首页
</body>
</html>

8.11、 基本的目录结构

8.12、配置Tomcat

8.12.1 点击Run、选择Edit Configurations…

8.12.2 基本信息配置

8.12.3 点击apply

8.13 启动tomcat

8.14 启动成功进入首页

8.15、如何实现请求转发页面跳转

8.15.1 创建controller 控制请求跳转

TestJsp.java

package com.zyz.mvc.controller;

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

/**
 * @author zyz
 * @version 1.0
 * @data 2022/11/29 10:13
 * @Description:
 */
@Controller
public class TestJsp {
    @RequestMapping("/success")
    public String success(){
        return "success";
    }

}

8.15.2 创建跳转后页面

改页面要创建在templates下、否则模板解析器解释不到

<%--
  Created by IntelliJ IDEA.
  User: Lenovo
  Date: 2022/11/29
  Time: 10:27
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>success</title>
</head>
<body>
<h1>这里是成功页面</h1>
</body>
</html>

8.15.3 跳转连接

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: Lenovo
  Date: 2022/11/29
  Time: 10:11
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
这是我的首页
<a href="${pageContext.request.contextPath}/success">success.jsp</a>
</body>
</html>

8.15.4 页面效果

8.15.5 点击跳转

8.16、最终完整的项目结构介绍

目录结构、请确保目录结构正确、否则也会导致运行项目失败

8.17、JSP模板解析和HTML模板解析对比

HTML模板解析

   <!-- 配置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>

这篇的主要目的是锻炼使用Result风格的接口开发。数据是假数据(并未真正连接到数据库)、页面使用BootStrap大致构建一下(不至于那么丑)

项目地址:F:\workspace\SpringMVC代码\SpringMVC-rest

项目的搭建过程、如何配置Tomcat过程略、改专栏系列之前有些。此处不做赘述

9、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查

9.1、项目结构

9.2、源码

9.2.1 web.xml文件

<?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">


    <!--配置过滤器编码-->
    <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>

    <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>

    <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
        <init-param>
            <!-- contextConfigLocation为固定值 -->
            <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>

9.2.2 springMVC.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--扫描组件-->
    <context:component-scan base-package="com.zyz.mvc"></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>

    <!--配置视图控制器-->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
    <mvc:view-controller path="/addEmployee" view-name="employee_add"></mvc:view-controller>

    <!--开放对静态资源的访问-->
    <mvc:default-servlet-handler/>

    <!--开启mvc的注解驱动-->
    <mvc:annotation-driven/>

</beans>

9.2.3 EmployeeController.java

package com.zyz.mvc.controller;

import com.zyz.mvc.dao.EmployeeDao;
import com.zyz.mvc.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Collection;

/**
 * @author zyz
 * @version 1.0
 * @data 2022/11/28 22:00
 * @Description:
 */
@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;


    /**
     * 查询所有员工信息
     * @param model
     * @return
     */
    @RequestMapping(value = "/employee",method = RequestMethod.GET)
    public String getAllEmployee(Model model){
        Collection<Employee> employeeList = employeeDao.getAll();
        model.addAttribute("employeeList",employeeList);
        return "employee_list";
    }

    /**
     * 删除员工信息
     * @param id
     * @return
     */
    @RequestMapping(value = "/employee/{id}",method = RequestMethod.DELETE)
    public String deleteEmployee(@PathVariable("id") Integer id ){
        employeeDao.delete(id);
        return "redirect:/employee";
    }

    /**
     * 添加用户信息
     * @param employee
     * @return
     */
    @RequestMapping(value = "/employee",method = RequestMethod.POST)
    public String addEmployee(Employee employee){
        employeeDao.save(employee);
        return "redirect:/employee";
    }

    /**
     * 跳转修改员工信息
     * @param id
     * @param model
     * @return
     */
    @RequestMapping(value = "/employee/{id}",method = RequestMethod.GET)
    public String toUpdateEmployee(@PathVariable("id") Integer id,Model model){
        Employee employee = employeeDao.get(id);
        model.addAttribute("employee",employee);
        return "employee_update";
    }


    /**
     * 修改用户信息
     * @param employee
     * @return
     */
    @RequestMapping(value = "/employee",method = RequestMethod.PUT)
    public String upDateEmployee(Employee employee){
        employeeDao.save(employee);
        return "redirect:/employee";
    }


}

9.2.4 EmployeeDao.java

假数据模拟

package com.zyz.mvc.dao;

import com.zyz.mvc.pojo.Employee;
import org.springframework.stereotype.Repository;

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

/**
 * @author zyz
 * @version 1.0
 * @data 2022/11/28 21:56
 * @Description:
 */
@Repository
public class EmployeeDao {
    private static Map<Integer, Employee> employees = null;

    static {
        employees = new HashMap<Integer, Employee>();
        employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
        employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
        employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
        employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
        employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
    }

    private static Integer initId = 1006;

    public void save(Employee employee) {
        if (employee.getId() == null) {
            employee.setId(initId++);
        }
        employees.put(employee.getId(), employee);
    }

    public Collection<Employee> getAll() {
        return employees.values();
    }

    public Employee get(Integer id) {
        return employees.get(id);
    }

    public void delete(Integer id) {
        employees.remove(id);
    }
}

9.2.5 Employee.java

实体类 使用Lombok

package com.zyz.mvc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @author zyz
 * @version 1.0
 * @data 2022/11/28 21:54
 * @Description:
 */
@Data
@AllArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;

}

9.2.6 employee_list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>员工信息</title>
    <style type="text/css">
        a {
            text-decoration: none;
        }
    </style>
    <!-- 新 Bootstrap 核心 CSS 文件 -->
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">
    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>员工信息----------显示所有员工信息</small>
                </h1>
            </div>
        </div>
    </div>

    <div class="row ">
        <div class="col-md-4 column">
            <a class="btn btn-primary btn-sm" th:href="@{/addEmployee}">添加员工</a>

        </div>

        <div class="col-md-4 column">

        </div>

    </div>

    <div class="row clearfix">
        <div class="col-md-12 column">
            <table id="mydataTable" class="table table-hover table-striped">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>姓名</th>
                    <th>邮箱</th>
                    <th>性别</th>
                    <th colspan="2">操作</th>
                </tr>
                </thead>
                <!--查询用户处理-->
                <tbody>
                <tr th:each="employee : ${employeeList}">
                    <td th:text="${employee.id}"></td>
                    <td th:text="${employee.lastName}"></td>
                    <td th:text="${employee.email}"></td>
                    <td>
                        <div th:switch="${employee.gender}">
                            <span th:case="1">男</span>
                            <span th:case="0">女</span>
                        </div>
                    </td>
                    <td>
                        <a class="btn btn-danger btn-xs" @click="deleteEmployee"
                           th:href="@{'/employee/'+${employee.id}}">删除</a>
                        <a class="btn btn-primary btn-xs" th:href="@{'/employee/'+${employee.id}}">修改</a>

                    </td>

                </tr>
                </tbody>
            </table>
        </div>
    </div>

</div>


<form id="deleteForm" method="post">
    <input type="hidden" name="_method" value="DELETE">
</form>

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
    var vue = new Vue({
        el: "#mydataTable",
        methods: {
            deleteEmployee: function (event) {
                var deleteForm = document.getElementById("deleteForm");
                deleteForm.action = event.target.href;
                deleteForm.submit();
                event.preventDefault();
            }
        }

    })
</script>

</body>
</html>

9.2.7 employee_add.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>添加</title>
    <!-- 新 Bootstrap 核心 CSS 文件 -->
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>添加员工</small>
                </h1>
            </div>
        </div>
    </div>
    <form th:action="@{/employee}" method="post">

        <div class="form-group">
            <label>姓名</label>
            <input type="text" class="form-control" name="lastName">
        </div>
        <div class="form-group">
            <label>邮箱</label>
            <input type="text" class="form-control" name="email">
        </div>
        <div class="form-group">
            <label>性别 </label>
            <input type="radio" name="gender" value="1" checked>男
            <input type="radio" name="gender" value="0">女
        </div>
        <input type="submit" class="btn btn-default" value="添加">

    </form>
</div>
</div>

</body>
</html>

9.2.8 employee_update.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>修改</title>
    <!-- 新 Bootstrap 核心 CSS 文件 -->
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>


<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>修改信息</small>
                </h1>
            </div>
        </div>
    </div>
    <form th:action="@{/employee}" method="post">
        <input type="hidden" name="_method" value="put">
        <input type="hidden" name="id" th:value="${employee.id}">
        <div class="form-group">
            <label>姓名</label>
            <input type="text" class="form-control" name="lastName" th:value="${employee.lastName}">
        </div>
        <div class="form-group">
            <label>邮箱</label>
            <input type="text" class="form-control" name="email" th:value="${employee.email}">
        </div>
        <div class="form-group">
            <label >性别 </label>
            <input type="radio" name="gender" value="1" th:field="${employee.gender}">男
            <input type="radio" name="gender" value="0" th:field="${employee.gender}">女
        </div>
        <input type="submit" class="btn btn-default" value="保存">

    </form>
</div>
</div>


</body>
</html>

9.2.9 index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <style type="text/css">
        a{
            text-decoration: none;
            color: black;
            font-size: 18px;
        }
        h3{

            width: 180px;
            height: 38px;
            margin:100px auto;
            text-align: center;
            line-height: 38px;
            background: deepskyblue;
            border-radius: 5px;

        }

    </style>
</head>
<body>

<h3>
    <a th:href="@{/employee}">查看所有员工信息</a><br>
</h3>
<h3>
    <a th:href="@{/addEmployee}">添加员工</a>
</h3>


</body>
</html>

9.3、实现的效果

9.3.1 首页

9.3.2 全部员工信息

9.3.3 添加员工

9.3.4 删除员工

9.3.5 修改信息

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

HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity, ResponseEntity

10、HttpMessageConverter @RequestBody 、@ResponseBody 、RequestEntity、ResponseEntity

10.1、@RequestBody

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

<form th:action="@{/testRequestBody}" method="post">
    姓名:<input type="text" name="username">
    密码:<input type="password" name="password">
    性别:<input type="text" name="sex">
    <button type="submit" >提交</button>
</form>

    /**
     * 测试requestBody
     * @param requestBody
     * @return
     */
    @RequestMapping(value = "/testRequestBody", method = RequestMethod.POST)
    public String testRequestBody(@RequestBody String requestBody) {
        System.out.println(requestBody);
        return "success";
    }

测试结果

10.2、RequestEntity

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

<form th:action="@{/testRequestEntity}" method="post">
    姓名:<input type="text" name="username">
    密码:<input type="password" name="password">
    <button type="submit" >提交</button>
</form>

    /**
     * 测试requestEntity
     * @param requestEntity
     * @return
     */
    @RequestMapping(value = "/testRequestEntity", method = RequestMethod.POST)
    public String testRequestEntity(RequestEntity<String> requestEntity) {
        System.out.println("请求头:" + requestEntity.getHeaders());
        System.out.println("请求体:" + requestEntity.getBody());
        return "success";
    }

测试结果

10.3、@ResponseBody

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

<a th:href="@{/testResponse}">测试response</a>

    /**
     * 测试response
     * @param response
     * @throws IOException
     */
    @RequestMapping("/testResponse")
    public void testResponse(HttpServletResponse response) throws IOException {
        response.getWriter().print("hello,Resopnse");
    }

测试结果

10.4、SpringMVC处理json

@ResponseBody处理json的步骤:

● 导入jackson的依赖

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

● 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串

<mvc:annotation-driven />

● 在处理器方法上使用@ResponseBody注解进行标识
● 将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串

/**
 * 如果返回数组对象   返回一个对象
 * @return
 */
@RequestMapping("/testResponseUser")
@ResponseBody
public List<User> testResponseUser() {
    User user1 = new User("张三", "男");
    User user2 = new User("李四", "男");
    ArrayList<User> list = new ArrayList<>();
    list.add(user1);
    list.add(user2);
    return list;

}

测试结果

提示:可以是对象数组、也可以是对象

10.5、SpringMVC处理ajax

使用到了vue、axios。需要下载对应的文件

请求链接:

<div id="app">
    <a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理axios</a>
</div>

<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">
    new Vue({
        el:"#app",
        methods:{
            testAxios:function (event){
                axios({
                    method:'post',
                    url:event.target.href,
                    params:{
                        username:"张三",
                        password:"123456"
                    }
                }).then(function (res){
                    alert(res.data)
                })

                event.preventDefault();
            }
        }
    })
</script>

控制器方法

/**
 * 测试axios
 * @param username
 * @param password
 * @return
 */
@RequestMapping("/testAxios")
@ResponseBody
public String testAxios(String username,String password){
    System.out.println("姓名:"+username+",密码:"+password);
    return "hello,axios";
}

测试结果

10.6、@RestController注解

我终于深刻理解RestController和Controller两者的使用场景

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

10.7、ResponseEntity

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

该文对纯小白不友好

11、文件上传和下载

11.1、文件下载

<a th:href="@{/testDown}">下载</a>

    /**
     * 测试下载
     * @param session
     * @return
     * @throws IOException
     */
    @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;
    }

测试结果

11.2、文件上传

文件上传要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息

● 添加依赖

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

● 在SpringMVC的配置文件中添加配置:

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

● 控制器方法:

这里对存储的文件名进行了处理、存储地址可以自定义

/**
 * 测试上传
 * @param photo
 * @param session
 * @return
 * @throws IOException
 */
@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");
    System.out.println(photoPath);
    File file = new File(photoPath);
    if(!file.exists()){
        file.mkdir();
    }
    String finalPath = photoPath + File.separator + fileName;
    //实现上传功能
    photo.transferTo(new File(finalPath));
    return "success";
}

测试效果

12、拦截器的使用

12.1、拦截器的配置

● 拦截器用于拦截控制器方法的执行
● 拦截器需要实现HandlerInterceptor
● 拦截器必须在SpringMVC的配置文件中进行配置

1.1 和1.2 是对所有的请求进行拦截;1.3 对排出的请求不在拦截

12.1.1 不使用注解的情况

创建拦截器类

/**
 * 拦截器
 * @author zyz
 * @version 1.0
 * @data 2022/12/18 14:15
 * @Description:
 */
public class FirstInterceptor implements HandlerInterceptor {
    /**
     * 方法执行前
     * CTRL+O 重写的快捷键
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor--->:preHandle");

        /**
         * 如果返回false、视图页面不出效果,由源码得知、走到一般返回了。没有到视图解析那一步
         */
        return true;
    }

    /**
     * 方法执行后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("FirstInterceptor--->:postHandle");

    }

    /**
     * 视图渲染后执行
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("FirstInterceptor--->:afterCompletion");

    }
}

在SpringMVC中进行配置

mvc:interceptors

12.1.2 使用注解的方式

只需要在拦截器类上添加注解@Component。

@Component
public class FirstInterceptor implements HandlerInterceptor {

在配置文件中使用

mvc:interceptors

12.1.3 自定义拦截

/*拦截当前目录下的请求。/**拦截所有的请求。这里的exclude-mapping表示排除拦截

<!-- 
    以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,
    通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/*"/>
            <mvc:exclude-mapping path="/"/>
            <ref bean="firstInterceptor"></ref>
        </mvc:interceptor>
    </mvc:interceptors>

12.2、拦截器的三个抽象方法

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

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

12.3、多个拦截器的执行顺序

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

至于为啥是反序执行、根据源码可知是通过i-- 的形式

配置文件中配置拦截器、可以通过普通的bean也可以通过注解形式

mvc:interceptors

preHandle()都返回true的时候

preHandle()都返回有一个为false的时候

13、异常处理器

13.1、基于配置的异常处理

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

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

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

1、编写一个方法,里边运算异常。2、调用该异常方法,会自动抛出异常信息到指定页面

<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>

13.2、基于注解的异常处理

```
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {

//@ExceptionHandler用于设置所标识方法处理的异常
@ExceptionHandler(ArithmeticException.class)
//ex表示当前请求处理中出现的异常对象
public String handleArithmeticException(Exception ex, Model model){
    model.addAttribute("ex", ex);
    return "error";
}

}

相关文章
|
6月前
|
XML Java 数据格式
爆肝15000字!Spring框架基础详解!
        因为开学要做项目的原因,花了整整一天的事件来复习spring的知识,并且对Spring中常用的东西进行总结,红字部分是相对重要的,大家可以多注意!如果本文有写的不对或者不明确的地方还希望大家可以在评论中指出。
|
3月前
|
XML 缓存 Java
一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】
文章提供了一份详尽的Mybatis使用教程,涵盖了Mybatis的简介、环境搭建、基本操作、配置解析、日志使用、分页、注解开发、多对一和一对多关系处理、动态SQL以及缓存机制等方面的内容,并提供了相应的代码示例和测试用例。
一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】
|
11月前
|
设计模式 前端开发 Java
手码两万余字,SpringMVC 包教包会(一)
手码两万余字,SpringMVC 包教包会
46 0
|
11月前
|
JSON 前端开发 Java
手码两万余字,SpringMVC 包教包会(二)
手码两万余字,SpringMVC 包教包会(二)
36 0
|
11月前
|
前端开发 安全 Java
手码两万余字,SpringMVC 包教包会(三)
手码两万余字,SpringMVC 包教包会(三)
40 0
|
11月前
|
JSON 前端开发 fastjson
手码两万余字,SpringMVC 包教包会(四)
手码两万余字,SpringMVC 包教包会(四)
26 0
|
负载均衡 Java 应用服务中间件
2w字Spring Cloud最全面试题整理,全是硬货
2w字Spring Cloud最全面试题整理,全是硬货
156 1
|
Java 编译器
类和对象(两万字超详解析)(四)
类和对象(两万字超详解析)
72 0
|
Java 编译器
类和对象(两万字超详解析)(二)
类和对象(两万字超详解析)
51 0
|
存储 Java 编译器
类和对象(两万字超详解析)(一)
类和对象(两万字超详解析)
83 0