一文吃透SpringMVC

news2024/11/14 12:17:09

一、SpringMVC简介

1、什么是MVC

MVC是一种软件架构模式(是一种软件架构设计思想,不止Java开发中用到,其它语言也需要用到),它将应用分为三块:

  • M:Model(模型),负责业务处理及数据的收集
  • V:View(视图),负责数据的展示
  • C:Controller(控制器),负责调度。它是一个调度中心,它来决定什么时候调用Model来处理业务,什么时候调用View视图来展示数据

图片

MVC架构模式的描述:前端浏览器发送请求给web服务器,web服务器中的Controller接收到用户的请求,Controller负责将前端提交的数据进行封装,然后Controller调用Model来处理业务,当Model处理完业务后会返回处理之后的数据给Controller,Controller再调用View来完成数据的展示,最终将结果响应给浏览器,浏览器进行渲染展示页面。

分享一份大彬精心整理的大厂面试手册,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~

需要的小伙伴可以自行下载

http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd

2、MVC架构模式与三层模型的区别

三层模型就是由Controller控制器和View视图组成的表现层,将Model数据模型拆封为业务层和与数据库交互的持久层。

图片

MVC架构模式与三层模型的区别?

  • MVC和三层模型都采用了分层结构来设计应用程序,都是降低耦合度,提高扩展力,提高组件复用性

  • 区别在于他们的关注点不同

    • 三层模型更加关注业务逻辑组件的划分
    • MVC架构模式关注的是整个应用程序的层次关系和分离思想
  • 现代的开发方式大部分都是MVC架构模式结合三层模型一起用

3、什么是SpringMVC

  • SpringMVC是一个实现了MVC架构模式的Web框架,底层基于Servlet实现

  • SpringMVC已经将MVC架构模式实现了,因此只要我们是基于SpringMVC框架写代码

  • Spring框架中有一个子项目叫做Spring Web,Spring Web子项目当中包含很多模块

    • Spring MVC
    • Spring WebFlux
    • Spring Web Services
    • Spring Web Flow
    • Spring WebSocket
    • Spring Web Services Client
  • Spring架构图如下,其中Web中的servlet指的就是Spring MVC

图片

二、HelloWorld程序

1、pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xc</groupId>
    <artifactId>springmvc-xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <dependencies>
        <!--springmvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!--servletAPI-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--spring5和thymeleaf整合-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

2、springmvc.xml

组件扫描。spring扫描这个包中的类,将这个包中的类实例化并纳入IoC容器的管理。

视图解析器。视图解析器(View Resolver)的作用主要是将Controller方法返回的逻辑视图名称解析成实际的视图对象。视图解析器将解析出的视图对象返回给DispatcherServlet,并最终由DispatcherServlet将该视图对象转化为响应结果,呈现给用户。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描-->
    <context:component-scan base-package="com.xc.controller"/>
    <!--视图解析器-->
    <bean id="thymeleafViewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <!--作用于视图渲染的过程中,可以设置视图渲染后输出时采用的编码字符集-->
        <property name="characterEncoding" value="UTF-8"/>
        <!--如果配置多个视图解析器,它来决定优先使用哪个视图解析器,它的值越小优先级越高-->
        <property name="order" value="1"/>
        <!--当 ThymeleafViewResolver 渲染模板时,会使用该模板引擎来解析、编译和渲染模板-->
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息,加载模板并对其进行解析-->
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!--设置模板文件的位置(前缀)-->
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <!--设置模板文件后缀(后缀),Thymeleaf文件扩展名不一定是html,也可以是其他,例如txt,大部分都是html-->
                        <property name="suffix" value=".html"/>
                        <!--设置模板类型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
                        <property name="templateMode" value="HTML"/>
                        <!--用于模板文件在读取和解析过程中采用的编码字符集-->
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

3、配置web.xml文件

  • Spring MVC是一个web框架,在javaweb中谁来负责接收请求,处理请求,以及响应呢?当然是Servlet
  • 在SpringMVC框架中已经为我们写好了一个Servlet,它的名字叫做:DispatcherServlet,我们称其为前端控制器
  • 既然是Servlet,那么它就需要在web.xml文件中进行配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <!--配置前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--手动设置springmvc配置文件的路径及名字-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!--为了提高用户的第一次访问效率,建议在web服务器启动时初始化前端控制器-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- /* 表示任何一个请求都交给DispatcherServlet来处理 -->
        <!-- / 表示当请求不是xx.jsp的时候,DispatcherServlet来负责处理本次请求-->
        <!-- jsp本质就是Servlet,因此如果请求是jsp的话,应该走它自己的Servlet,而不应该走DispatcherServlet -->
        <!-- 因此我们的 url-pattern 使用 / -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  1. 接收客户端的HTTP请求:DispatcherServlet监听来自Web浏览器的HTTP请求,Tomcat已经将请求数据解析为Request对象。
  2. 处理请求的URL:DispatcherServlet将请求的URL与处理程序进行匹配,确定要调用哪个控制器(Controller)来处理此请求。
  3. 调用相应的控制器:DispatcherServlet将请求发送给找到的控制器处理,控制器将执行业务逻辑,然后返回一个模型对象(Model)。
  4. 渲染视图:DispatcherServlet将调用视图引擎,将模型对象呈现为用户可以查看的HTML页面。
  5. 返回响应给客户端:DispatcherServlet将为用户生成的响应发送回浏览器,响应可以包括表单、JSON、XML、HTML以及其它类型的数据。

4、html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
hello world
</body>
</html>

5、执行Controller

@Controller
public class HelloController {
    @RequestMapping("/test")
    public String test(){
        return "success";
    }
}

配置Tomcat

图片

启动tomcat,调用test

图片

三、RequestMapping注解

@RequestMapping 注解是 Spring MVC 框架中的一个控制器映射注解,用于将请求映射到相应的处理方法上。具体来说,它可以将指定 URL 的请求绑定到一个特定的方法或类上,从而实现对请求的处理和响应。

RequestMapping的出现位置

  • 通过源码可以看到RequestMapping注解只能出现在类上或者方法上
  • 当然类上和方法上也可以同时出现,类上是公共的,方法上是独有的

图片

1、value属性

1.1 基础使用
  • value属性是该注解最核心的属性,value属性填写的是请求路径,也就是说通过该请求路径与对应的控制器的方法绑定在一起
  • value属性是一个字符串数组,表示可以提供多个路径,也就是说,多个不同的请求路径可以映射同一个控制器的同一个方法
  • value属性和path属性互为别名,两个属性一样

图片

如下两个请求路径都可以正常访问到控制器的方法上

  • http://localhost:8080/springmvc/hello/test1
  • http://localhost:8080/springmvc/hello/test2
@Controller
@RequestMapping("/hello")
public class RequestMappingController {
    @RequestMapping(value = {"/test1","test2"})
    public String test(){
        return "success";
    }
}
1.2 Ant风格(模糊匹配路径)

value是可以用来匹配路径的,路径支持模糊匹配,我们把这种模糊匹配称之为Ant风格

  • ?:表示任意的单个字符
  • *:表示任意的0个或多个字符
  • **:表示任意的一层或多层目录(只能使用xxx/**的方式)

匹配?例子

@RequestMapping("/x?z/testValueAnt")
public String testValueAnt(){
    return "success";
}
1.3 路径占位符(@PathVariable)

普通的请求路径:http://localhost:8080/springmvc/login?username=admin&password=123&age=20

restful风格的请求路径:http://localhost:8080/springmvc/login/admin/123/20

如果使用restful风格的请求路径,在控制器中应该如何获取请求中的数据呢?

不加@PathVariable路径变量注解会抛500异常

@RequestMapping(value = "/testRestful/{id}/{username}/{age}")
public String testRestful(
        @PathVariable("id") int id,
        @PathVariable("username") String username,
        @PathVariable("age") int age) {
    System.out.println(id + "," + username + "," + age);
    return "success";
}

2、method属性

2.1 基础使用
  • 如果前端发送请求的方式和后端的处理方式不一致时,会出现405错误
  • HTTP状态码405,这种机制的作用是:限制客户端的请求方式,以保证服务器中数据的安全
  • SpringMVC使用RequestMapping注解的method属性来实现限制请求方式

图片

  • 通过RequestMapping源码可以看到,method属性也是一个数组
  • 数组中的每个元素是RequestMethod,而RequestMethod是一个枚举类型的数据

图片

举例,只允许get和post请求方式,否则报错405

@RequestMapping(value="/login", method = {RequestMethod.GET,RequestMethod.POST})
public String testMethod(){
    return "success";
}
2.2 衍生xxxMapping注解

SpringMVC提供了另外一些注解,使用更加的方便

  • GetMapping:要求前端必须发送get请求
  • PutMapping:要求前端必须发送put请求
  • DeleteMapping:要求前端必须发送delete请求
  • PatchMapping:要求前端必须发送patch请求

举例,两种方式效果一样,对比衍生注解更加简洁

//@RequestMapping(value="/login", method = RequestMethod.POST)
@PostMapping("/login")
public String testMethod(){
    return "success";
}
2.3 web的请求方式

前端向服务器发送请求的方式包括哪些?共9种

  1. GET:获取资源,只允许读取数据,不影响数据的状态和功能

    • 使用URL中传递参数或者在HTTP请求的头部使用参数,服务器返回请求的资源
  2. POST:向服务器提交资源,可能还会改变数据的状态和功能

    • 通过表单等方式提交请求体,服务器接收请求体后,进行数据处理
  3. PUT:更新资源,用于更新指定的资源上所有可编辑内容

    • 通过请求体发送需要被更新的全部内容,服务器接收数据后,将被更新的资源进行替换或修改
  4. DELETE:删除资源,用于删除指定的资源

    • 将要被删除的资源标识符放在URL中或请求体中
  5. HEAD:请求服务器返回资源的头部

    • 与 GET 命令类似,但是所有返回的信息都是头部信息,不能包含数据体
    • 主要用于资源检测和缓存控制
  6. OPTIONS:请求获得服务器支持的请求方法类型,以及支持的请求头标志

    • OPTIONS则返回支持全部方法类型的服务器标志
    • 主要用于跨域检查
  7. PATCH:部分更改请求

    • 当被请求的资源是可被更改的资源时,请求服务器对该资源进行部分更新,即每次更新一部分
  8. TRACE:服务器响应输出客户端的 HTTP 请求,主要用于调试和测试

  9. CONNECT:建立网络连接,通常用于加密 SSL/TLS 连接

注意:

  • 使用超链接以及原生的form表单只能提交get和post请求
  • put、delete、head请求可以使用发送ajax请求的方式来实现

GET和POST的区别

  • get请求比较适合从服务器端获取数据
  • post请求比较适合向服务器端传送数据
  • get请求支持缓存。也就是说当第二次发送get请求时,会走浏览器上次的缓存结果,不再真正的请求服务器
  • post请求不支持缓存。每一次发送post请求都会真正的走服务器

3、params属性

对于RequestMapping注解来说:

  1. value属性是一个数组,只要满足数组中的任意一个路径,就能映射成功
  2. method属性也是一个数组,只要满足数组中任意一个请求方式,就能映射成功
  3. params属性也是一个数组,不过要求请求参数必须和params数组中要求的所有参数完全一致后,才能映射成功

图片

params属性的4种用法

  1. @RequestMapping(value=“/login”, params={“username”, “password”})

    • 请求参数中必须包含username 和 password,才能与当前标注的方法进行映射
  2. @RequestMapping(value=“/login”, params={“!username”, “password”})

    • 请求参数中不能包含username参数,但必须包含password参数,才能与当前标注的方法进行映射
  3. @RequestMapping(value=“/login”, params={“username=admin”, “password”})

    • 请求参数中必须包含username参数,并且参数的值必须是admin,另外也必须包含password参数,才能与当前标注的方法进行映射
  4. @RequestMapping(value=“/login”, params={“username!=admin”, “password”})

    • 请求参数中必须包含username参数,但参数的值不能是admin,另外也必须包含password参数,才能与当前标注的方法进行映射

4、headers属性

  • headers和params原理相同,用法也相同
  • 当前端提交的请求头信息和后端要求的请求头信息一致时,才能映射成功

headers属性的4种用法

  1. @RequestMapping(value=“/login”, headers={“Referer”, “Host”})

    • 请求头信息中必须包含Referer和Host,才能与当前标注的方法进行映射
  2. @RequestMapping(value=“/login”, headers={“!Referer”, “Host”})

    • 请求头信息中不能包含Referer参数,但必须包含Host参数,才能与当前标注的方法进行映射
  3. @RequestMapping(value=“/login”, headers={“Referer=xxx”, “Host”})

    • 请求头信息中必须包含Referer参数,并且参数的值必须是xxx,另外也必须包含Host参数,才能与当前标注的方法进行映射
  4. @RequestMapping(value=“/login”, headers={“Referer!=xxx”, “Host”})

    • 请求头信息中必须包含Referer参数,但参数的值不能是xxx,另外也必须包含Host参数,才能与当前标注的方法进行映射

四、获取请求参数

1、原生Servlet API

前端表单提交数据

图片

F12查询提交数据方式

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-yAF8dMYH-1724679463424)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

后端控制器获取数据

@PostMapping(value="/register")
public String register(HttpServletRequest request){
    // 通过当前请求对象获取提交的数据
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    String sex = request.getParameter("sex");
    String[] hobbies = request.getParameterValues("hobby");
    String intro = request.getParameter("intro");
    System.out.println(username + "," + password + 
     "," + sex + "," + Arrays.toString(hobbies) + "," + intro);
    return "success";
}

这样通过Servlet原生的API获取到提交的数据。但是这种方式不建议使用,因为方法的参数HttpServletRequest依赖Servlet原生API,Controller的测试将不能单独测试,必须依赖web服务器才能测试。

2、RequestParam注解

2.1 value属性

RequestParam注解作用:将请求参数与方法上的形参映射

@PostMapping(value = "/register")
public String register(
        @RequestParam(value = "username") String a,
        @RequestParam(value = "password") String b,
        @RequestParam(value = "sex") String c,
        @RequestParam(value = "hobby") String[] d,
        @RequestParam(name = "intro") String e
) {
    System.out.println(a);
    System.out.println(b);
    System.out.println(c);
    System.out.println(Arrays.toString(d));
    System.out.println(e);
    return "success";
}

@RequestParam注解的两个属性value和name,互为别名,作用相同

图片

发送请求时提交的数据是:name1=value1&name2=value2,则这个注解应该这样写:@RequestParam(value=“name1”)、@RequestParam(value=“name2”)

2.2 required属性
  • required属性用来设置该方法参数是否为必须的
  • 默认情况下,这个参数为 true,表示方法参数是必需的。如果请求中缺少对应的参数,则会抛出异常
  • 可以将其设置为false,false表示不是必须的,如果请求中缺少对应的参数,则方法的参数为null

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-3E33LFrJ-1724679463426)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

举例,添加了一个 age 形参,没有指定 required 属性时,默认是true,表示必需的

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-rXOlyyv2-1724679463426)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

但前端表单中没有年龄age,报错如下

图片

2.3 defaultValue属性
  • defaultValue属性用来设置形参的默认值
  • 当没有提供对应的请求参数或者请求参数的值是空字符串""的时候,方法的形参会采用默认值

举例,age属性设置为非必须,当前端不传值时候,默认年龄为18岁

图片

3、根据形参名获取

如果方法形参的名字和提交数据时的name相同,则@RequestParam可以省略

@PostMapping(value="/register")
public String register(String username, String password, String sex, 
 String[] hobby, String intro){
    System.out.println(username + "," + password + "," + sex + "," 
     + Arrays.toString(hobby) + "," + intro);
    return "success";
}

4、根据实体类接收

  • 在SpringMVC中也可以使用POJO类/JavaBean实体类来接收请求参数
  • 不过有一个非常重要的要求:实体类的属性名必须和请求参数的参数名保持一致
@PostMapping("/register")
public String register(User user){
    System.out.println(user);
    return "success";
}
  • 底层的实现原理:反射机制

    • 先获取User对象实例,没有则反射实例化
    • 然后获取请求参数的名字,通过请求参数名字拼接出set属性名的方法名
    • 最后User实例和set属性方法反射给属性赋值
  • 请求参数是否可以赋值到JavaBean对应的属性上,不是取决于属性名,而是setter方法名

5、RequestHeader注解

该注解的作用是:将请求头信息映射到方法的形参上

对于RequestHeader注解来说,也有三个属性:value、required、defaultValue,和RequestParam一样

@PostMapping("/register")
public String register(User user, 
 @RequestHeader(value="Referer", required = false, defaultValue = "") String referer){
    System.out.println(user);
    System.out.println(referer);
    return "success";
}

6、CookieValue注解

该注解的作用是:将请求提交的Cookie数据映射到方法的形参上

对于CookieValue注解来说,也有三个属性:value、required、defaultValue,和RequestParam一样

@GetMapping("/register")
public String register(User user,
 @CookieValue(value="id", required = false, defaultValue = "110") String id){
    System.out.println(user);
    System.out.println(id);
    return "success";
}

7、请求的中文乱码问题

7.1 get请求乱码

get请求数据在URI后面提交,这个乱码问题怎么解决呢?

解决办法是找到 CATALINA_HOME/config/server.xml文件,找到其中配置端口号的标签,在该标签中添加URIEncoding="UTF-8

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-JWBKQPvG-1724679463429)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

但是对于高版本的Tomcat服务器来说,是不需要设置的,例如Tomcat10,Tomcat9,有如下的默认配置,在默认情况下URIEncoding使用的就是UTF-8的编码方式。

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-vaoTsUk7-1724679463430)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

但对于低版本的Tomcat服务器,例如:Tomcat8,URIEncoding的默认配置是ISO-8859-1

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-5YzQjxrH-1724679463431)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

7.2 post请求乱码

post请求是解决请求体的中文乱码问题

同样,对于高版本的Tomcat10服务器来说,针对请求体中的字符编码也是配置好的,默认也是采用了UTF-8,web.xml配置如下

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-w8cMFovL-1724679463432)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

一定要注意:Tomcat9以及之前的版本,以上的配置是没有的

解决方法:web.xml中配置mvc自带的乱码过滤器

<!--配置SpringMVC自带的乱码过滤器-->
<filter>
    <filter-name>encoding</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>
    <!-- 
     设置forceEncoding为true,就是强制设置编码的意思
  forceEncoding为true就会设置forceRequestEncoding和forceResponseEncoding为true
  这样,如下源码的两个if条件成立,就会设置utf-8编码了
  -->
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

源码解析

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-Lpk9I64t-1724679463434)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

图片

五、Servlet的三个域对象

请求域:request、会话域:session、应用域:application

三个域都有以下三个方法:

// 向域中存储数据
void setAttribute(String name, Object obj);

// 从域中读取数据
Object getAttribute(String name);

// 删除域中的数据
void removeAttribute(String name); 

主要是通过:setAttribute + getAttribute方法来完成在域中数据的传递和共享

1、request域对象

  • request对象代表了一次请求,一次请求一个request

  • 使用请求域的业务场景

    • 在A资源中通过转发的方式跳转到B资源
    • 因为是转发,所以从A到B是一次请求
    • 如果想让A资源和B资源共享同一个数据,可以将数据存储到request域中
  • 在request域中共享数据有以下几种方式

    • 使用原生Servlet API方式
    • 使用Model接口
    • 使用Map接口
    • 使用ModelMap类
    • 使用ModelAndView类

使用原生Servlet API方式

@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
    // 向request域中存储数据
    request.setAttribute("testRequestScope", "在SpringMVC中使用原生Servlet API实现request域数据共享");
    return "view";
}

使用Model接口

@RequestMapping("/testModel")
public String testModel(Model model){
    // 向request域中存储数据
    model.addAttribute("testRequestScope", "在SpringMVC中使用Model接口实现request域数据共享");
    return "view";
}

使用Map接口

@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
    // 向request域中存储数据
    map.put("testRequestScope", "在SpringMVC中使用Map接口实现request域数据共享");
    return "view";
}

使用ModelMap类

@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
    // 向request域中存储数据
    modelMap.addAttribute("testRequestScope", "在SpringMVC中使用ModelMap实现request域数据共享");
    return "view";
}

Model、Map、ModelMap的关系?

  • 输出打印Model、Map、ModelMap的Class,底层实例化的对象都是:BindingAwareModelMap
  • BindingAwareModelMap的继承结构

图片

BindingAwareModelMap继承ModelMap实现Model,而ModelMap又实现了Map接口

图片

使用ModelAndView类

  • 为了更好的体现MVC架构模式,提供了一个类:ModelAndView。这个类的实例封装了Model和View
  • 也就是说这个类既封装业务处理之后的数据,也体现了跳转到哪个视图
  • 使用它也可以完成request域数据共享
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    // 创建“模型与视图对象”
    ModelAndView modelAndView = new ModelAndView();
    // 绑定数据
    modelAndView.addObject("testRequestScope", "在SpringMVC中使用ModelAndView实现request域数据共享");
    // 绑定视图
    modelAndView.setViewName("view");
    // 返回
    return modelAndView;
}

注意:

  1. 方法的返回值类型不是String,而是ModelAndView对象
  2. ModelAndView不是出现在方法的参数位置,而是在方法体中new的
  3. 需要调用addObject向域中存储数据
  4. 需要调用setViewName设置视图的名字

以上我们通过了五种方式完成了request域数据共享,这几种方式在底层DispatcherServlet调用我们的Controller之后,返回的对象都是ModelAndView。

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-Vor6J3iB-1724679463438)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

2、session域对象

  • session对象代表了一次会话

    • 从打开浏览器开始访问,到最终浏览器关闭,这是一次完整的会话
    • 每个会话session对象都对应一个JSESSIONID,而JSESSIONID生成后以cookie的方式存储在浏览器客户端
    • 浏览器关闭,JSESSIONID失效,会话结束
  • 使用会话域的业务场景

    • 登录成功后保存用户的登录状态

使用原生Servlet API方式

@RequestMapping("/testSessionScope1")
public String testServletAPI(HttpSession session) {
    // 向会话域中存储数据
    session.setAttribute("testSessionScope1", "使用原生Servlet API实现session域共享数据");
    return "view";
}

3、application域对象

  • application对象代表了整个web应用

    • 服务器启动时创建,服务器关闭时销毁
    • 对于一个web应用来说,application对象只有一个
  • 使用应用域的业务场景

    • 记录网站的在线人数

使用原生Servlet API方式

@RequestMapping("/testApplicationScope")
public String testApplicationScope(HttpServletRequest request){
    // 获取ServletContext对象
    ServletContext application = request.getServletContext();
    // 向应用域中存储数据
    application.setAttribute("applicationScope", "我是应用域当中的一条数据");
    return "view";
}

六、HttpMessageConverter消息转换器

  • HttpMessageConverter是Spring MVC中非常重要的一个接口
  • 翻译为:HTTP消息转换器。该接口下提供了很多实现类,不同的实现类有不同的转换方式

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-d84O9ngu-1724679463439)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

转换器是HTTP协议与Java程序中的对象之间的互相转换。

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-j97QsYJ6-1724679463440)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

1、Form表单转换器和默认转换器

请求体中的数据是如何转换成user对象的,底层实际上使用了HttpMessageConverter接口的其中一个实现类FormHttpMessageConverter。

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-QHvkI884-1724679463441)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

通过上图可以看出FormHttpMessageConverter是负责将请求协议转换为Java对象的。

2、@ResponseBody

首页面AJAX请求获取数据,非跳转页面Controller

2.1 Servlet原生API方式
// 有返回值
@RequestMapping(value = "/hello1")
public String hello1(HttpServletResponse response) throws IOException {
    response.getWriter().print("hello");
    return null;
}

// 无返回值
@RequestMapping(value = "/hello2")
public void hello2(HttpServletResponse response) throws IOException {
    response.getWriter().print("hello");
}

页面展示

图片

注意:如果采用这种方式响应,则和 springmvc.xml 文件中配置的视图解析器没有关系,不走视图解析器了

2.2 @ResponseBody注解方式

这里的"hello"不是逻辑视图名了,而是作为响应体的内容进行响应。直接输出到浏览器客户端

程序中使用的消息转换器是:StringHttpMessageConverter,为什么会启用这个消息转换器呢?

因为你添加@ResponseBody这个注解

@Controller
public class HelloController {
    @RequestMapping(value = "/hello")
    @ResponseBody
    public String hello(){
        // 由于你使用了 @ResponseBody 注解
        // 以下的return语句返回的字符串则不再是“逻辑视图名”了
        // 而是作为响应协议的响应体进行响应。
        return "hello";
    }
}

通常AJAX请求需要服务器给返回一段JSON格式的字符串,可以返回JSON格式的字符串吗?当然可以,代码如下:

@Controller
public class HelloController {

    @RequestMapping(value = "/hello")
    @ResponseBody
    public String hello(){
        return "{\"username\":\"zhangsan\",\"password\":\"1234\"}";
    }
}

页面展示

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fsvg%2Bxml%2C%253C%253Fxml%20version%3D’1.0’%20encoding%3D’UTF-8’%253F%253E%253Csvg%20width%3D’1px’%20height%3D’1px’%20viewBox%3D’0%200%201%201’%20version%3D’1.1’%20xmlns%3D’http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg’%20xmlns%3Axlink%3D’http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink’%253E%253Ctitle%253E%253C%2Ftitle%253E%253Cg%20stroke%3D’none’%20stroke-width%3D’1’%20fill%3D’none’%20fill-rule%3D’evenodd’%20fill-opacity%3D’0’%253E%253Cg%20transform%3D’translate(-249.000000%2C%20-126.000000&pos_id=img-PxW95Bb4-1724679463443)’ fill=‘%23FFFFFF’%3E%3Crect x=‘249’ y=‘126’ width=‘1’ height=‘1’%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)

此时底层使用的消息转换器还是:StringHttpMessageConverter

那如果在程序中是一个POJO对象,怎么将POJO对象以JSON格式的字符串响应给浏览器呢?

方式一:自己写代码将POJO对象转换成JSON格式的字符串,用上面的方式直接return即可

方式二:启用MappingJackson2HttpMessageConverter消息转换器

2.3 MappingJackson2HttpMessageConverter(JSON转换器)

启动JSON消息转换器需要两个步骤

第一步:引入jackson依赖,可以将java对象转换为json格式字符串

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

第二步:开启注解驱动,会自动装配一个消息转换器:MappingJackson2HttpMessageConverter

<mvc:annotation-driven/>

@ResponseBody最经典用法如下:

@RequestMapping(value = "/hello")
@ResponseBody
public User hello(){
    User user = new User("zhangsan", "18");
    return user;
}

将POJO对象转换成JSON格式的字符串,响应给前端

3、@RestController

为了方便,Spring MVC中提供了一个注解@RestController。这一个注解代表了:@Controller + @ResponseBody @RestController标注在类上即可。被它标注的Controller中所有的方法上都会自动标注@ResponseBody

@RestController
public class HelloController {
    @RequestMapping(value = "/hello")
    public User hello(){
        User user = new User("zhangsan", "18");
        return user;
    }
}

4、@RequestBody

作用是直接将请求体传递给Java程序

4.1 &拼接参数

在没有使用这个注解的时候:

@RequestMapping("/save")
public String save(User user){
    // 执行保存的业务逻辑
    userDao.save(user);
    // 保存成功跳转到成功页面
    return "success";
}

当请求体提交的数据是:

username=zhangsan&password=1234&email=zhangsan@powernode.com

那么Spring MVC会自动使用 FormHttpMessageConverter消息转换器,将请求体转换成user对象

当使用这个注解的时候:这个注解只能出现在方法的参数上

@RequestMapping("/save")
public String save(@RequestBody String requestBodyStr){
    System.out.println("请求体:" + requestBodyStr);
    return "success";
}

Spring MVC仍然会使用 FormHttpMessageConverter消息转换器,将请求体直接以字符串形式传递给requestBodyStr变量。

4.2、JSON格式参数
  • 如果请求体是JSON格式字符串,可以将其转化为POJO对象
  • 此时必须使用@RequestBody注解来完成
  • 底层使用的消息转换器是:MappingJackson2HttpMessageConverter
  • 启动步骤与@ResponseBody一样,引入jackson依赖、开启注解驱动
@RequestMapping("/send")
@ResponseBody
public String send(@RequestBody User user){
    System.out.println(user);
    System.out.println(user.getUsername());
    System.out.println(user.getPassword());
    return "success";
}

5、RequestEntity

这个类的实例封装了整个请求协议:包括请求行、请求头、请求体所有信息

@RequestMapping("/send")
@ResponseBody
public String send(RequestEntity<User> requestEntity){
    System.out.println("请求方式:" + requestEntity.getMethod());
    System.out.println("请求URL:" + requestEntity.getUrl());
    HttpHeaders headers = requestEntity.getHeaders();
    System.out.println("请求的内容类型:" + headers.getContentType());
    System.out.println("请求头:" + headers);

    User user = requestEntity.getBody();
    System.out.println(user);
    System.out.println(user.getUsername());
    System.out.println(user.getPassword());
    return "success";
}

执行结果:

图片

6、ResponseEntity

  • 用该类的实例可以封装响应协议,包括:状态行、响应头、响应体
  • 如果你想定制属于自己的响应协议,可以使用该类
@Controller
public class UserController {
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        if (user == null) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        } else {
            return ResponseEntity.ok(user);
        }
    }
}

七、异常处理器

1、默认异常处理器

方法执行过程中出现了异常,跳转到对应的视图,在视图上展示友好信息

默认处理器DefaultHandlerExceptionResolver核心方法:

图片

当请求方式和处理方式不同时,DefaultHandlerExceptionResolver的默认处理态度是:

图片

2、自定义异常处理器

2.1 跳转错误页面
@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler
    public String exceptionHandler(Exception e, Model model){
        model.addAttribute("e", e);
        return "error";
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>出错了</title>
</head>
<body>
<h1>出错了,请联系管理员!</h1>
<div th:text="${e}"></div>
</body>
</html>
2.2 返回错误响应对象
@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public ResponseEntity<String> exceptionHandler(Exception e, Model model) {
        // 这里先判断拦截到的Exceptiion是不是我们自定义的异常类型
        if (e instanceof MyException) {
            MyException myException = (MyException) e;
            return ResponseEntity.status(500).body(myException.getMeg());
        } else {
            // 如果拦截的异常不是我们自定义的异常(例如:数据库主键冲突)
            return ResponseEntity.status(500).body("服务器端异常");
        }
    }
}

八、拦截器

1、拦截器概述

拦截器作用是在请求到达控制器之前或之后进行拦截,可以对请求和响应进行一些特定的处理

拦截器可以用于很多场景下

  1. 登录验证:对于需要登录才能访问的网址,使用拦截器可以判断用户是否已登录,如果未登录则跳转到登录页面
  2. 权限校验:根据用户权限对部分网址进行访问控制,拒绝未经授权的用户访问
  3. 请求日志:记录请求信息,例如请求地址、请求参数、请求时间等,用于排查问题和性能优化
  4. 更改响应:可以对响应的内容进行修改,例如添加头信息、调整响应内容格式等

2、拦截器和过滤器的区别

过滤器更注重在请求和响应的流程中进行处理,可以修改请求和响应的内容,例如设置编码和字符集、请求头、状态码等

拦截器则更加侧重于对控制器进行前置或后置处理,在请求到达控制器之前或之后进行特定的操作,例如打印日志、权限验证等

Filter、Servlet、Interceptor、Controller的执行顺序:

图片

3、拦截器的创建与基本配置

定义拦截器

  • 实现org.springframework.web.servlet.HandlerInterceptor 接口,共有三个方法可以进行选择性的实现

    • preHandle:处理器方法调用之前执行(返回true放行,false拦截)
    • postHandle:处理器方法调用之后执行
    • afterCompletion:渲染完成后执行(无论是否抛异常最终必会执行)
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("处理器方法前调用");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("处理器方法后调用");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("渲染完成后调用");
    }
}

基本配置

springmvc.xml配置如下

<mvc:interceptors>
    <bean class="com.xc.interceptors.MyInterceptor"/>
</mvc:interceptors>
<!-- 或者 -->
<mvc:interceptors>
    <ref bean="myInterceptor"/>
</mvc:interceptors>

添加组件扫描

图片

注意:对于这种基本配置来说,拦截器是拦截所有请求的

4、多个拦截器执行顺序

如果所有拦截器preHandle都返回true

按照springmvc.xml文件中配置的顺序,自上而下调用 preHandle

<mvc:interceptors>
    <ref bean="interceptor1"/>
    <ref bean="interceptor2"/>
</mvc:interceptors>

执行顺序:

图片

如果其中一个拦截器preHandle返回false

<mvc:interceptors>
    <ref bean="interceptor1"/>
    <ref bean="interceptor2"/>
</mvc:interceptors>

如果interceptor2的preHandle返回false,执行顺序:

图片

规则:只要有一个拦截器preHandle返回false,所有postHandle都不执行。但返回false的拦截器的前面的拦截器按照逆序执行afterCompletion。

最后分享一份大彬精心整理的大厂面试手册,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~

需要的小伙伴可以自行下载

http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2076887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Phone X│“齐刘海”里到底搭载了哪些传感器?

揭开 “齐刘海”面纱 随着iPhone X的发布&#xff0c;3D人脸识别功能一度成为人们口中津津乐道的新科技&#xff0c;下面就让SITRI的专业团队带领大家揭开人脸识别的神秘面纱。 先从苹果iPhone X说起&#xff0c;让我们看一看这个可爱的“齐刘海”里到底搭载了哪些传感器&am…

杀软对抗 ----> 简简单单免杀某60

在开始之前还是宇宙免责 本Blog讨论的技术问题仅限于学习用&#xff0c;严禁用于非授权情况下执行攻击活动&#xff0c;如未授权攻击所造成一切后果由攻击者独自承担&#xff0c;所展示的代码仅用于杀软对抗学习&#xff0c;测试环境并非真实环境&#xff0c;并无对任何杀软有任…

我的sql我做主!Mysql 的集群架构详解之组从复制、半同步模式、MGR、Mysql路由和MHA管理集群组

目录 Mysql 集群技术一、Mysql 在服务器中的部署方法1.1 在Linux下部署mysql1.1.1 安装依赖性&#xff1a;1.1.2 下载并解压源码包1.1.3 源码编译安装mysql1.1.4 部署mysql 二、Mysql的组从复制2.1 配置mastesr2.2 配置salve2.3 当有数据时添加slave22.4 延迟复制2.5 慢查询日志…

医疗器械法规标准相关资料

文章目录 前言如何查找法规文件与标准1. 法规清单2. 医疗器械法规文件汇编常用链接常见网站微信公众号前言 在前文 医疗器械软件相关法律法规与标准 中介绍了在软件设计过程常见的法规与标准,并给出部分标准如何查找和下载的方法,但是上文中列举的部分不全面,真实在产品设计…

springboot公众号模板消息推送

文章目录 参考1、微信公众平台测试号管理1.1 访问微信公众平台测试账号页面1.2 获取appID和appsecret1.3 扫码二维码添加测试号1.4 添加模版消息 2、集成微信SDK2.1 引入微信工具包2.2 添加配置文件 3、API调用3.1 发送消息模版的实现3.2 测试类调用3.3 效果展示 4、回调配置回…

通过IMB看高效裁员

高效裁员是企业在面临经营压力或战略调整时不得不采取的措施之一。为了确保裁员过程既高效又尽量减少负面影响,可以遵循以下步骤和策略: 一、明确裁员目标和计划 分析需求:首先,企业需要明确裁员的原因,比如经营困难、业务重组、技术升级等,并基于这些原因确定裁员的范…

Centos系统二进制安装mysql5.7.44、添加环境变量、复制启动脚本、初始化数据库、设置用户密码

MySQL :: Download MySQL Community Server (Archived Versions) https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz 删除默认存在的mariadb-libs rpm -e mariadb-libs --nodeps 安装mysql cd /usr/local/src/ && …

Redis中的 大/热 key问题 ,如何解决(面试版)

big key 什么是 big key? big key&#xff1a;就是指一个内存空间占用比较大的键(Key) 造成的问题&#xff1a; 内存分布不均。在集群模式下&#xff0c;不同 slot分配到不同实例中&#xff0c;如果大 key 都映射到一个实例&#xff0c;则分布不均&#xff0c;查询效率也…

拿下英语翻译!这四款在线翻译功不可没!

作为一个刚踏入职场的小白&#xff0c;我最近在为工作中的英语翻译问题头疼不已。不过&#xff0c;经过一番尝试和比较&#xff0c;我找到了几款翻译工具&#xff0c;它们在帮我解决翻译难题上表现得相当不错。今天&#xff0c;就让我以一个职场新手的身份&#xff0c;来跟大家…

OpenCV几何图像变换(6)计算反转仿射变换函数invertAffineTransform()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 反转一个仿射变换。 该函数计算由 23 矩阵 M 表示的逆仿射变换&#xff1a; [ a 11 a 12 b 1 a 21 a 22 b 2 ] \begin{bmatrix} a_{11} & a…

【超音速 专利 CN202110438812.4】广州超音速自动化科技股份有限公司

申请号CN202110438812.4公开号&#xff08;公开&#xff09;CN113390879A申请日2021.09.14申请人&#xff08;公开&#xff09;广州超音速自动化科技股份有限公司(833753)发明人&#xff08;公开&#xff09;张俊峰&#xff08;总); 罗国和; 陈夏 原文摘要 本发明公开了一种涂…

【上】王树森《搜索引擎技术》- 课程笔记(概要、相关性)

课程合集&#xff1a;ShusenWang的个人空间-ShusenWang个人主页-哔哩哔哩视频 (bilibili.com) 课件地址&#xff1a;GitHub - wangshusen/SearchEngine: 搜索引擎原理 目录 概要 1、搜索引擎的基本概念 曝光和点击 垂搜vs通搜 课程安排 2、决定搜索满意度的因素&#…

某音作品列表,视频列表

声明&#xff1a;文章仅用于学习交流,如有侵权请联系删除 今天分享下某音app作品列表采集方法&#xff0c;我只详细说一下大步骤&#xff0c;细节就不多说了&#xff0c;留着大家去试吧 我们通过Fiddler 快捷方式 配置好代理 打开抖音进行抓包&#xff0c;随便找个达人打开主…

喜羊羊让你Pyecharts快速上手(实例+代码)

以下内容&#xff0c;皆为原创&#xff0c;制作实属不易&#xff0c;感谢大家的关注和点赞。 一.Pyecharts是什么 具体网址&#xff1a;pyecharts - A Python Echarts Plotting Library built with love. Pyecharts 是一个用于生成 Echarts 图表的 Python 库。Echarts 是由百度…

行业级API集成案例,巩固你的知识

在当今高度互联的世界&#xff0c;企业依靠无缝数据交换和简化的工作流程蓬勃发展。API&#xff08;应用程序编程接口&#xff09;已成为实现这一目标的秘密武器&#xff0c;可实现各种应用程序之间的强大集成。本文深入探讨了不同行业中 API 集成的真实示例&#xff0c;让您更…

四、5 下标引用、函数调用、结构成员(操作符)

&#xff08;1&#xff09;下标引用 [ ] (2)函数调用 ( ) (3)结构成员

python setup.py build install的GCC版本报错

在进行一些python三方库编译的时候&#xff0c;有时候会因为环境中的GCC版本导致编译错误&#xff0c;比如在mmdet3d&#xff0c;mmcv-full等库的使用中。 Your compiler (g 4.8.5) may be ABI-incompatible with PyTorch! Please use a compiler that is ABI-compatible with …

windows虚拟机VMware共享文件

1、设置本机电脑共享目录 2、设置所有人可连接 3、记录共享文件夹路径 4、设置当前用户密码 5、在虚拟机内映射驱动 6、在虚拟机内添入路径 7、输入用户名和密码 8、链接成功

BaseCTF-web-Week1

写在前面&#xff1a; 题目类型还是比较全&#xff0c;也都是基础题型&#xff0c;适合刚入门 CTF 的萌新学习&#xff0c;我之前在学校实验室预备队招新赛中也有出过一些类似的基础题&#xff0c;欢迎大家参考。 SNERT预备队招新CTF体验赛-Web&#xff08;SWCTF&#xff09;ht…

ozon恢复产品,Ozon恢复销售在六月份暂时关闭的品类

在6月份&#xff0c;Ozon发布通知&#xff1a;《Ozon将限制中国卖家电子产品、汽车摩配、DIY工具的销售》今天&#xff0c;我们收到通知&#xff0c;6月被关闭的品类将被重新开放销售。 Ozon恢复销售在六月份暂时关闭的品类地址&#xff1a;m6z.cn/5H6fQR 6月份的消息一出&…