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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 文章提供了一份全面的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";
}

}

相关文章
|
3月前
|
XML 缓存 Java
一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】
文章提供了一份详尽的Mybatis使用教程,涵盖了Mybatis的简介、环境搭建、基本操作、配置解析、日志使用、分页、注解开发、多对一和一对多关系处理、动态SQL以及缓存机制等方面的内容,并提供了相应的代码示例和测试用例。
一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】
|
11月前
|
设计模式 前端开发 Java
手码两万余字,SpringMVC 包教包会(一)
手码两万余字,SpringMVC 包教包会
44 0
|
11月前
|
前端开发 安全 Java
手码两万余字,SpringMVC 包教包会(三)
手码两万余字,SpringMVC 包教包会(三)
39 0
|
11月前
|
JSON 前端开发 Java
手码两万余字,SpringMVC 包教包会(二)
手码两万余字,SpringMVC 包教包会(二)
35 0
|
11月前
|
JSON 前端开发 fastjson
手码两万余字,SpringMVC 包教包会(四)
手码两万余字,SpringMVC 包教包会(四)
25 0
|
负载均衡 Java 应用服务中间件
2w字Spring Cloud最全面试题整理,全是硬货
2w字Spring Cloud最全面试题整理,全是硬货
154 1
|
存储 缓存 算法
JVM专题整理,三万字分析(一)
JVM专题整理,三万字分析
142 1
|
Arthas 存储 缓存
JVM专题整理,三万字分析(三)
JVM专题整理,三万字分析
146 0