1.4W字!让我带你读懂springmvc的世界!

news2024/11/24 5:59:57

目录

一.前提了解

1.tomcat和servlet的关系?

2.springmvc想要实现web开发必须满足的条件是什么?

二.什么是SpringMVC

三.基于SpringMVC创建web项目

①创建项目并选择依赖

 ②设置热部署(部分代码改动不需要手动重新run即可生效)

四.理解前后端分离的开发过程

五.SpringMVC实现web开发

1.详解用户端返回的响应

@Controller

@ResponseBody

重定向和转发

转发

重定向

转发和重定向的区别:(M)

自定义返回类型

@RestController

@RequestMapping

 2.详解服务端接收用户端的请求

2.1关于请求路径和请求头中的参数

@Pathvirable

使用postman对测试结果进行分析:

@RequestHeader

@CookieValue

@SessionAttribute

2.2关于请求参数

无注解的请求参数

@RequestParam

@RequestPart

@RequestBody

通过配置类设置编程式的配置方案

设置后端路径的统一前缀

设置拦截器

统一异常处理

统一的结果处理:

关于responseBodyAdvice的作用


一.前提了解

在我们真正的去讲述springmvc之前,我们先明确以下两个问题:

1.tomcat和servlet的关系?

我们都知道,servlet是实现动态页面的技术,servlet是进行web开发的规范,而tomcat是servlet容器,它在符合servlet规范的基础上对外提供了web服务器和端口号,实现对servlet的统一管理;同时可以接收用用户的请求,将请求发送给servlet,并在servlet给出响应之后将响应发送给客户端

2.springmvc想要实现web开发必须满足的条件是什么?

通过上面的论述,我们能清晰理解的是,tomcat满足了servlet规范,并在此基础上进行了web开发,springmvc如果想要实现web开发,也必须满足servlet规范。

二.什么是SpringMVC

SpringMVC是进行web开发的框架,它同样依托于springboot框架(springboot内包含了springmvc),受益于springboot框架中约定大于配置的原则,springboot内部默认配置了springmvc的文件路径,并对springmvc进行了很多默认的配置(不需要用户自己手动配置),springmvc内置了满足servlet规范的web服务器(相当于定制化的服务器)(对servlet进行了进一步的封装),而如果我们想要使用springmvc进行web开发,只需要满足springmvc的使用规范即可。

使用springmvc进行web开发的目的也很明了:使web开发更方便,提高web开发的效率

三.基于SpringMVC创建web项目

①创建项目并选择依赖

 ②设置热部署(部分代码改动不需要手动重新run即可生效)

 

③禁用JMX:因为如果不对其进行排除会导致在项目启动时报错,虽然这个报错不影响我们项目的实现,但是规范化起见,我们还是加上

 ④禁用tomcat,取而代之undertow(非必须选项,换是因为undertow的效率略高于tomcat)

⑤修改编码集

四.理解前后端分离的开发过程

springMVC 的MVC 是由以下三部分组成:①Model (模型)②View(视图) ③ Controller(控制器) 

我们结合web请求和响应的过程来理解开发过程和springmc的三个组成部分:

用户发送http请求,通过controller层结合model层分析请求信息并给出响应响应,将响应的数据返回给view层,最后通过view层给出http响应 。

结合具体的放方法对此进行分析:

事实上专业的逻辑应该是这样的:
 

处理流程
DispatcherServlet 的处理流程可以分为以下几个步骤:

接收客户端请求
        当客户端发送请求时,DispatcherServlet 会接收并处理该请求。接收请求的方式取决于 DispatcherServlet 的配置,通常情况下,它会将请求映射到一个 URL,然后监听该 URL 的请求。

创建请求对象
        DispatcherServlet 会根据客户端请求创建一个请求对象,该对象中包含了客户端请求的所有信息,例如请求方法、请求头、请求参数等。

处理请求映射
DispatcherServlet 会将请求映射到相应的控制器进行处理。请求映射是通过 HandlerMapping 进行的,HandlerMapping 负责将请求映射到一个或多个控制器,以便选择最合适的控制器进行处理。

调用控制器
        DispatcherServlet 会调用相应的控制器进行处理,控制器会根据请求参数和业务逻辑进行相应的处理,并返回一个 ModelAndView 对象。

渲染视图
        DispatcherServlet 会将 ModelAndView 对象传递给视图解析器(ViewResolver),视图解析器会根据 ModelAndView 中的视图名称来解析相应的视图对象。然后,DispatcherServlet 将模型数据传递给视图对象,以便渲染视图。最终,视图对象会生成相应的响应结果并返回给客户端。

 

五.SpringMVC实现web开发

1.详解用户端返回的响应

@Controller

@controller表示被这个注解修饰的类是一个bean(一般只能用来修饰类),并且这个bean负责处理web请求和响应

@ResponseBody

@ResponseBody表示响应正文,即规定返回值的类型(既可以修饰类,又可以修饰方法,修饰类代表所有的方法都设置返回值类型,修饰方法代表只有这个方法规定好返回值的类型),@ResponseBody规定被其修饰的方法的返回值以特定的数据格式进行返回,默认的返回形式是json

重定向和转发

如果没有使用@ResponseBody来规定好返回值类型的格式,的默认的返回值类型是String,并且代表一个路径:这个路径一般分为两种应用场景:转发和重定向

转发

语法格式:forward:/+路径

抓包查看特征:

①只有一次请求和响应 响应的资源是一个html页面

 

重定向

语法格式:"redirect:/+路径”

抓包查看特征:

第一次请求: 

 第一次响应:

第二次请求:

第二次响应:

转发和重定向的区别:(M)

1.重定向访问服务器两次,转发只访问服务器一次。

2.转发页面的URL不会改变,而重定向地址会改变

3.转发只能转发到自己的web应用内,重定向可以重定义到任意资源路径。

4.转发相当于服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行目标文件,两个文件(当前文件和目标文件)属于同一次请求,前后页 共用一个request,可以通过此来传递一些数据或者session信息,request.setAttribute()和 request.getAttribute()。而重定向会产生一个新的request,不能共享request域信息与请求参数

5.由于转发相当于服务器内部方法调用,所以转发后面的代码仍然会执行(转发之后记得return);重定向代码执行之后是方法执行完成之后进行重定向操作,也就是访问第二个请求,如果是方法的最后一行进行重定向那就会马上进行重定向(重定向也需要return)。
 

自定义返回类型

除了使用@ResponseBody设置返回值类型和进行转发和重定向之外,还能实现自定义的返回值类型,操作如下:

我们举一个例子来进行说明:我们传输给客户端一个.doc对象(网络资源下载)

1.创建文件路径对象

2.读取路径中的字节数组

3.设置自定义的返回对象

@Controller
public class SelfController {
    @GetMapping("/object1")
    public ResponseEntity test() throws IOException {
        //创建返回值
        //传输字节码文件
        Path p=new File("D:\\大物实验报告\\42109211014_20221018142451.doc").toPath();
        byte[]bytes= Files.readAllBytes(p);
      return   ResponseEntity.ok().header("content-type", "application/msword").body(bytes);
    }
}

我们使用路径进行访问

@RestController

我们暂且可以将其作用简单理解为:@Controller和@Responsebody的组合注解

使用方法:加载类前面

@RequestMapping

@RequestMapping代表请求路径,该注解既可以加到类上,也能加到方法上如果类上和方法上同时加了这个注解,则请求路径表示为:类路径+方法路径

使用方法:

部分属性:

●value:定义request请求的映射地址

●method:定义地request址请求的方式,包括【GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.】默认接受get请求,如果请求方式和定义的方式不一样则请求无法成功。

●params:定义request请求中必须包含的参数值。

 2.详解服务端接收用户端的请求

2.1关于请求路径和请求头中的参数

@Pathvirable

使用方式:用于修饰形参

@Pathvirable 作用:标识请求路径中的动态参数(从使用@RequestMapping中请求路径中读取被@Pathvirable 修饰的方法形参变量名一致的变量,将其读取至方法中相同变量名的形参变量中。)

代码演示:

@RestController
public class PathTest {
    @RequestMapping("/test/{id}")
    public Object test(@PathVariable Long id){
        //创建容器
        HashMap<String, Long> stringLongHashMap = new HashMap<>();
        stringLongHashMap.put("id", id);
        return  stringLongHashMap;
    }

使用postman对测试结果进行分析:

 @pathvirable修饰的变量类型可以对请求路径传来的变量进行校验,如果请求路径中的参数和参数的类型不符,会报400错误

@RequestHeader

作用:通过绑定请求头中的字段名,获取请求头的字段信息

使用示例:

   @RequestMapping("/header")
    public Object getHeader(@RequestHeader("user-agent") String headers){
        HashMap<String, String> stringLongHashMap = new HashMap<>();
        stringLongHashMap.put("user-agent", headers);
        return  stringLongHashMap;
    }

@CookieValue

作用:通过绑定cookie中的键名,获取cookie中的信息

使用方式:

    @RequestMapping("/cookie")
    public Object getCookie(@CookieValue("JSESSIONID") String cookie){
        HashMap<String, String> stringLongHashMap = new HashMap<>();
        stringLongHashMap.put("my-cookie", cookie);
        return  stringLongHashMap;

@SessionAttribute

在讲解这个之前,我们先简要说一下cookie和session之间的关系:session保存在服务端,服务端用它来保存用户信息,我们可以将session理解为键值对,键为随机生成的sessionId,值为当前用户的session对象;cookie保存到客户端,也是用来保存用户信息,再校验用户信息时根据sessionId在服务端匹配对应的sessoin对象,进行身份校验

@SessionAttribute用来处获取请求参数中的session信息(根据sessionId在服务端获取session对象,之后进行校验用户信息)

使用实例:
 

 @RequestMapping("/login")
    public Object info(HttpServletRequest request){
        //通过session存储session信息
        HttpSession session = request.getSession(true);
        //存储session信息
        session.setAttribute("user", "zhangsan");
        //创建容器并返回
        HashMap<String, String> map = new HashMap<>();
        map.put("user", "zhangsan");
        return map;
    }
    @RequestMapping("/check")
    public Object checkLogin(@SessionAttribute("user") String name){
        HashMap<String, String> map = new HashMap<>();
        map.put("user", name);
        return map;
    }

一般在进行登录校验时的大体流程如下:

首次登录时检查session对象是否存在(不存在则新创建一个session对象),之后根据前端传来的用户信息对其进行存储,返回响应,之后如果再进行其他操作(需要在用户登录的基础上)则需要根据sessionId从服务器中获取session对象进行校验

结果分析:

2.2关于请求参数

无注解的请求参数

 前端的请求参数通常分为四种数据格式进行传输:query-string ,表单类型的数据、json格式的数据和form-data类型的数据,而我们后端接收请求的java对象,我们将其分为简单数据类型(基本数据类型+包装类+String)和复杂数据类型(集合框架和自定义的类型),以下我们将从这两种数据类型进行分析;
我们先给出结论:无论是简单数据类型还是复杂数据类型,在没有注解的情况下后端能进行数据解析的只有query-string ,表单类型的数据、json格的数据,json类型的数据需要使用注解(@RequestBody),我们根据这个画一个表格对此进行说明:

我们分别对这几种前端数据类型进行分析:
 

@RequestParam

@RequestParam(只能用来修饰简单类型的形参)

参数:vlaue/name:用来标识请求参数的名称(请求路径中请求参数的名称),这个参数也可以不传,只要保证方法中的参数和请求路径参数完全一致即可

required:这个参数在请求参数中是否必须提供:FALSE代表不是必须提供,TRUE代表必须提供

参数类型类型不匹配或者要求必须提供的参数没有提供会报400错误

作用:将请求参数转化为控制器方法的形参

使用实例:
 

@RequestMapping("/requestB")
    public Object res(@RequestParam String name ,@RequestParam() Integer id){
        HashMap<String, String> map = new HashMap<>();
        map.put("name", name);
        map.put("id", id+"");
        return map;
    }

@RequestPart

@RequestPart一般用于用于将multipart/form-data类型数据映射到控制器处理方法的参数中

注解解析

  ① value:

    绑定的参数名称,参数值为String类型。

  ② name:

    绑定的参数名称,参数值为String类型。name和value可以同时使用,但两者的值需一致,否则会出现错误。(400)
  ③ required:

    请求头中是否必须包含指定的值,默认值为true。

    required为true时,如果请求头中缺少指定的值,则会抛出异常。

    required为false时,如果请求头中缺少指定的值,则会返回null。

使用用例:

    @RequestMapping("/upload")
    //上传文件
    public Object upload(@RequestPart MultipartFile head, User user) throws IOException {
       head.transferTo(new File("D:/上传的"+head.getOriginalFilename()));
        HashMap<String, Object> map = new HashMap<>();
        map.put("file", head);
        return map;

    }

文件上传可能出现的错误:系统找不到指定文件
java.io.FileNotFoundException: C:\Users\86131\AppData\Local\Temp\tomcat.8080.6906634590984434583\work\Tomcat\localhost\ROOT\upload_135c4883_3414_49af_b54e_39dbe063b0de_00000002.tmp (系统找不到指定的文件。)

我们去系统指定的目录中查看文件是否上传成功:

发现虽然报了错误,但是文件上传成功了:

在分析错误原因前我们先对文件上传的逻辑进行分析:客户端向服务端传输文件,文件首先报错到系统网卡然后部分文件信息保存到服务端内存,而有关文件内容将会在本地的一个临时目录中进行保存,如果我们调用transferTo(),会将默认保存的系统文件移动到我们指定的文件目录中,这种情况下原来临时目录的文件就没有了,所以当我们后面再调用有关文件信息的方法,首先在服务端的内存中进行查找,如果服务端内存没有这个信息,则需要到临时目录中进行查找,如果临时目录中没有这个文件,则会报以上的错误。

如何解决这个问题呢?

解决思路也比较清晰:在调用文件相关信息的时候,在transferTo()方法执行之前进行调用即可。

@RequestBody

我们前面有说到的是无论是无注解的请求参数还是使用@RequestParam注解,都不支持对json数据的解析,而@RequestBody能够实现对json数据的解析,不支持对其他数据类型的解析。

使用实例:

  @RequestMapping("/json")

    public Object jsonSend( @RequestBody User user){
        HashMap<String, String> map = new HashMap<>();
        map.put("name", user.getName());
        map.put("id", user.getId()+"");
        return map;
    }

通过配置类设置编程式的配置方案

我们知道可以通过设置配置文件的格式对springboot项目进行设置,但是通过配置文件设置比较麻烦或者设置通过配置文件无法实现的我们可以通过编程(设置配置类)来设置配置。通过@Configuration来实现这个功能:被@Configuration修饰的类会在springboot项目启动时进行加载配置,我们通过使用以下两个实例进行加载说明:

设置后端路径的统一前缀

功能:为所有的后端控制器添加统一的前缀(名为:api)

应用场景:在访问后端的请求路径时需要对其的部分路径的处理逻辑加一些统一的校验,为了区分前端路径和后端路径,我们统一为所有的控制器都加一个前缀.

我们给出代码实例:

因为我们进行的是有关web开发的配置,所以我们进行的配置要继承WebMvcConfigurer的接口

@Configuration
public class AppConfig implements WebMvcConfigurer {
    //调用路径匹配的api,为所有的后端逻辑添加前缀
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
      configurer.addPathPrefix("api", c->{
          //通过循环遍历所有的后端controller判断是否要加前缀(暂时设置所有的路径都为前缀)
          //如果需要在此处使某些控制器不加前缀,在此处应该加一些别的逻辑经即可
         return true;
      });
    }

我们在实际的路径中并没有api请求路径, 但是我们进行访问时必须通过“api”路径进行访问,否则直接报404

 

设置拦截器

我们首先了解一下web开发的三大组件:servlet(连接器)、listener(监听器)和filter(过滤器),我们需要明确的是:spring官方并没有提供拦截器,拦截器是springmvc提供的,但是其实现原理是和过滤器类似。

拦截器的处理逻辑如下:

 在客户端返送请求之前会通过拦截器preHander相关方法进行处理,preHander返回一个Boolean值,TRUE则继续向下执行,FALSE则直接返回;在controller层返回响应后同样也会通过拦截器,这时回调用postHander()方法,最后将响应返回给客户端,我们自定义的拦截器通常会重写以下三个方法:

1、preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。

2、postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。

3、afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。

我们设置拦截器的过程可以从以下三个方面进行设计分析:①自定义拦截器②设置拦截路径③设置拦截器的处理逻辑

我们给出一个拦截器中以下的三种代码:
①路径处理:

配置拦截路径支持模糊匹配:而进行配置的方式,一般是通过添加拦截路径和排除一些不拦截的路径来处理

/**:添加任意路径

/api/**:添加api下的任意路径

/api/*:添加api下的一层目录

设置排除路径:

/api/login:排除api下的login的目录

/api/register:排除api下的register目录

②自定义拦截器:

自定义拦截器并引用HandlerInterceptor接口,然后重写接口中的方法

③实现方法逻辑

在重写的方法中实现方法逻辑

完整代码:

@Configuration
public class AppConfig implements WebMvcConfigurer {
    //调用路径匹配的api,为所有的后端逻辑添加前缀
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
      configurer.addPathPrefix("api", c->{
          //通过循环遍历所有的后端controller判断是否要加前缀(暂时设置所有的路径都为前缀)
         return true;
      });
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //使用注册器进行注册过滤器
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/login")
                        .excludePathPatterns("/api/register");
    }
}
public class LoginInterceptor implements HandlerInterceptor {
    //设置请求逻辑:在请求前进行处理
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       //获取session
        HttpSession session = request.getSession(false);
        if(session!=null){
            String user = (String)session.getAttribute("user");
            if(user!=null){
                //判断是否是用户
                if(user.equals("admin")){
                    return true;
                }
            }
        }
        response.setStatus(401);

        return false;
    }
}

Interceptor 作用
日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等;
权限检查:如登录检测,进入处理器检测用户是否登录;
性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)
通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。

统一异常处理

如果我们通过浏览器进行请求时出现错误,极有可能把后端的报错信息全部显示到浏览器上,一方面这样存在极大的安全隐患(其他程序员根据报错可能会对我们后端的实现有一定的了解,从而进行破坏性操作),另一方面对用户的体验也并不是很友好。

因此我们需要进行统一的异常处理,将后端报错进行统一起来,我们进行如下操作:使用@ControllerAdvice注解进行控制器增强,使用@ExceptionHandle(异常类)进行统一的异常处理,@ExceptionHandle(异常类)的处理逻辑如下:根据括号中的异常类信息,当发生括号内的异常类中的报错信息之后,使用其下的方法进行报错的处理(起到的作用是异常处理的catch)

@ControllerAdvice
public class ExceptionHandle {
    //默认以json形式返回数据
    @ResponseBody
    //统一捕获异常类
    @ExceptionHandler(Exception.class)
    public Object handle(Exception e){
        HashMap<String, Object> map = new HashMap<>();
        map.put("code",500);
        map.put("message",e.getMessage());
        return map;
    }

需要注意的是:@ExceptionHandle(异常类)只能捕获其括号内的异常,一旦出了其括号内异常类的范围,还是会直接将错误信息直接返回给客户端:修改之后的错误信息如下:

@ControllerAdvice
public class ExceptionHandle {
    //默认以json形式返回数据
//    @ResponseBody
//    //统一捕获异常类
//    @ExceptionHandler(Exception.class)
//    public Object handle(Exception e){
//        HashMap<String, Object> map = new HashMap<>();
//        map.put("code",500);
//        map.put("message",e.getMessage());
//        return map;
//    }
    @ResponseBody
    //统一捕获异常类
    @ExceptionHandler(IOException.class)
    public Object handleIo(Exception e){
        HashMap<String, Object> map = new HashMap<>();
        map.put("code",500);
        map.put("message",e.getMessage());
        return map;
    }

统一的结果处理:

①自定义结果类

@Data
public class Result {
    private Boolean ok;
    private Object data;
    private String error;
}

②使用@ControllerAdvice统一处理结果:设置ok data(data在返回的body中直接处理) 和error

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //进行赋值处理
        Result result = new Result();
        result.setOk(true);
        result.setData(body);
        result.setError(null);
        return result;
    }
}

我们使用这个结果类访问以下代码:

import java.util.HashMap;

/**
 * @author tongchen
 * @create 2023-04-28 10:34
 */
@RestController
public class UserController {
    @RequestMapping("/user/{id}/{name}")
    public Object user(@PathVariable String name,@PathVariable Integer id ){
        HashMap<String, Object> map = new HashMap<>();
        map.put("user", name);
        return map;
    }
}

结果如下:

但是我们发现一个问题,在这个代码中,我们将返回信息除了data外直接写死了 ,我们思考一个问题:在真正的业务场景中,我们应该如何统一处理返回结果呢?(异常结果处理和统一结果处理)

A:修改返回类,取消统一设置返回方法,在具体的业务场景中具体返回结果,异常类单独返回结果

具体code及结果如下:

@Data
public class Result<T> {
    private Integer ok;//1代表成功 0代表失败
    private T data;
    private String msg;//正确或异常的信息
    public static <T>Result<T> success(T data){
        Result<T> result = new Result<>();
        result.setData(data);
        result.setOk(1);
        return result;
    }
    public static  <T>Result<T>error(String msg){
        Result<T> result = new Result<>();
        result.setOk(0);
       result.setMsg(msg);
        return result;
    }
}

 正常的结果类:

@RestController
public class UserController {
    @RequestMapping("/user/{id}/{name}")
    public Object user(@PathVariable String name,@PathVariable Integer id ){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("name",name);
        return Result.success(map);
    }
}

异常的结果类:

public class MyException extends Exception{
    public MyException(String message) {
        super(message);
    }
}
@RestController
public class ExceptionController {
    @RequestMapping("/test")
    public void test() throws MyException {
        throw new MyException("HAHAHH");
    }
}

异常的结果类处理:
 

@ControllerAdvice
public class ExceptionHandle {
    //默认以json形式返回数据
//    @ResponseBody
//    //统一捕获异常类
//    @ExceptionHandler(Exception.class)
//    public Object handle(Exception e){
//        HashMap<String, Object> map = new HashMap<>();
//        map.put("code",500);
//        map.put("message",e.getMessage());
//        return map;
//    }
    @ResponseBody
    //统一捕获异常类
    @ExceptionHandler(MyException.class)
    public Object handleIo(Exception e){
        HashMap<String, Object> map = new HashMap<>();
      return Result.error("服务器错误");
    }


}

正常结果:

 异常结果:

 

关于responseBodyAdvice的作用

 

 ResponseBodyAdvice 接口是在 Controller 执行 return 之后,在 response 返回给客户端之前,执行的对 response 的一些处理,可以实现对 response 数据的一些统一封装或者加密等操作。

  该接口一共有两个方法:

(1)supports  —— 判断是否要执行beforeBodyWrite方法,true为执行,false不执行  ——  通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。

(2)beforeBodyWrite  ——  对 response 处理的具体执行方法。

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

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

相关文章

工地烟火AI监控识别分析系统 yolov7

工地烟火AI监控识别分析系统通过yolov7网络模型技术&#xff0c;工地烟火AI监控识别分析系统对工地或者厂区现场监控区域内的烟火进行实时分析报警。YOLOv7 的发展方向与当前主流的实时目标检测器不同&#xff0c;研究团队希望它能够同时支持移动 GPU 和从边缘到云端的 GPU 设备…

请求重定向(forward)和请求转发(redirect)的区别详解(看这一篇就够了)

在Java中进行页面跳转的方式有两种&#xff1a;重定向和请求转发&#xff0c;但是两者的内部实现是完全不一样的&#xff0c;主要区别分为以下5种&#xff1a; 定义不同请求方式不同数据共享不同最终 URL 地址不同代码实现不同 1&#xff0c;定义不同 请求重定向&#xff08;f…

五面阿里Java岗,从小公司到阿里的面经总结

​​​​​​​ 面试 笔试常见的问题 面试常见的问题下面给的面试题基本都有。 1 手写代码&#xff1a;手写代码一般会考单例、排序、线程、消费者生产者 排序。 2 写SQL很常考察group by、内连接和外连接 2.面试1-5面总结 1&#xff09;让你自我介绍 2&#xff09;做两道算法…

【软考高级】2022年系统分析师综合知识

1.( )是从系统的应用领域而不是从系统用户的特定需要中得出的&#xff0c;它们可以是新的功能性需求&#xff0c;或者是对已有功能性需求的约束&#xff0c;或者是陈述特定的计算必须遵守的要求。 A.功能性需求 B. 用户需求 C.产品需求 D.领域需求 2.对于安全关键系…

红黑树理论详解与Java实现

文章目录 基本定义五大性质红黑树和2-3-4树的关系红黑树和2-3-4树各结点对应关系添加结点到红黑树注意事项添加的所有情况 添加导致不平衡叔父节点不是红色节点&#xff08;祖父节点为红色&#xff09;添加不平衡LL/RR添加不平衡LR/RL 叔父节点是红色节点&#xff08;祖父节点为…

知识图谱学习笔记——(四)知识图谱的抽取与构建

一、知识学习 声明&#xff1a;知识学习中本文主体按照浙江大学陈华钧教授的《知识图谱》公开课讲义进行介绍&#xff0c;并个别地方加入了自己的注释和思考&#xff0c;希望大家尊重陈华钧教授的知识产权&#xff0c;在使用时加上出处。感谢陈华钧教授。 &#xff08;一&…

汇编三、51单片机汇编指令1

1、指令格式 (1)举例&#xff1a;将立即数0x30送入累加器A MOV  A, #0x30 标号 操作码 目标地址&#xff0c;数据源 ;注解 (2)标号&#xff0c;注解可选项&#xff0c;不一定有。 2、指令执行时间和指令存储空间 (1)指令执…

谁是太阳膜界的真正王者?

小编一文教会你如何选择好的太阳膜 随着天气越来越热&#xff0c;有很多车友迫切的想为自己的爱车&#xff0c;贴上隔热膜&#xff0c;特别是新能源车主。 现在的新能源车都有很大的前挡玻璃和全景天窗&#xff0c;提升了爱车的档次和美观度。但是随之而来的隔热和安全也受到了…

文心一言创意图

文章目录 本土优化创意图成语和典故 本土优化 此前文心一格最让人诟病的就是那张“爱国猫”的图像了&#xff0c;十分离谱&#xff0c;让人一猜就是训练集的问题。 但百度作为全村的希望&#xff0c;对文心一言的优化也是肉眼可见的&#xff0c;迅速做了针对本土的优化&#…

5月新书预告

“读书不觉已春深&#xff0c;一寸光阴一寸金。”相信许多小伙伴儿都把这个五一假期安排的满满当当&#xff0c;还有一部分人抱着书本养精蓄锐、精进技能。小编也没闲着&#xff0c;为大家收集了几本精品新书。 《现代软件工程》是《持续交付》的作者David Farley的另一本力作&…

Codeforces Round 868 (Div. 2)A.B.C

A. A-characteristic 题目链接&#xff1a; Problem - A - Codeforces 题面&#xff1a; 题意&#xff1a; 有一个数组a&#xff0c;里面只存在1和-1&#xff0c;现在可以选择任意两个位置&#xff0c;但是不能重合&#xff0c;如果两个位置的数乘积为1&#xff0c;那么特点…

学生台灯什么牌子好对眼睛好?专业护眼灯的学生台灯分享

据报告统计&#xff0c;2022年我国儿童青少年总体近视率为52.7%&#xff0c;其中6岁儿童为14.3%&#xff0c;小学生为35.6%&#xff0c;初中生为71.1%&#xff0c;高中生为80.5%&#xff0c;这些数据让人不寒而栗&#xff01; 专家表示&#xff0c;导致儿童青少年近视的因素&am…

Shell脚本2

自定义局部变量 :定义在一个脚本文件中的变量 只能在这个脚本文件中使用的变量&#xff0c;局部变量 语法&#xff1a; var_namevalue 变量定义规则 变量名称可以有字母,数字和下划线组成, 但是不能以数字开头 等号两侧不能有空格 在bash环境中, 变量的默认类型都是字符串…

thinkphp+vue+html基于web的旅游景点酒店线路管理系统6722q

数据库分析 整个系统所包括的信息有景点信息、用户信息、酒店信息、旅行社信息、留言信息等。可将这些信息抽象为下列系统所需要的数据项和数据结构: 1.景点管理(编号&#xff0c;景点名称&#xff0c;景点等级&#xff0c;天气情况&#xff0c;位置&#xff0c;住宿&#xff0…

真无线耳机哪款性价比高?高性价比无线耳机排行榜

近几年&#xff0c;蓝牙耳机凭借使用便捷而受到广大用户的欢迎&#xff0c;逐渐取代有线耳机成为人们生活中必不可少的存在。下面&#xff0c;我来给大家推荐几款性价比高的蓝牙耳机&#xff0c;一起来看看吧。 一、南卡小音舱Lite2蓝牙耳机 参考价&#xff1a;299 蓝牙版本…

初识C++之特殊类设计与类型转换

目录 一、特殊类设计 1. 设计一个对象不能被拷贝的类 2. 设计一个只能在堆上创建对象的类 2.1 禁止构造函数 2.2 禁止析构函数 3. 设计一个只能在栈上创建对象的类 4. 设计一个不能被继承的类 5. 设计一个只能创建出一个对象的类&#xff08;单例模式&#xff09; 5.1…

开源世界的尴尬时刻

作者按&#xff1a;开源世界里全都是一片祥和、欣欣向荣吗&#xff1f;全都是热情开放、无私奉献吗&#xff1f;那当然不是。本文罗列几起尴尬世界&#xff0c;目的是让大家对开源世界有一个更全面更具体的认识&#xff0c;让大家对底层开源人的生活的心态有一个更切实际的了解…

ORBBEC(奥比中光)AstraPro相机在ROS2下的标定与D2C(标定与配准)

文章目录 1.rgb、depth相机标定矫正1.1.标定rgb相机1.2.标定depth相机1.3.rgb、depth相机一起标定&#xff08;效果重复了&#xff0c;但是推荐使用&#xff09;1.4.取得标定结果1.4.1.得到的标定结果的意义 1.5.IR、RGB相机分别应用标定结果1.5.1.openCV应用标定结果1.5.2.ros…

mac m1安装V2rayU

我原先window上使用的是V2rayN。mac系统使用的是V2rayU。 1.首先下载V2rayU 下载地址&#xff1a;https://github.com/yanue/V2rayU/releases 2.将下载的文件拉到application 安装后&#xff0c;点击运行&#xff0c;此时会弹出安全警告&#xff0c;需要在「系统偏好设置-安…

第4章-动态规划

第4章-动态规划 总分&#xff1a;100分 得分&#xff1a;100.0分 10.0 分 1 . 多选题 中等 10分 有关0-1背包问题,用c[i][j]描述子问题:1...i共i个物品,背包容量为j的最优值(装入背包的最大价值),则其子问题为:1...i-1共i-1个物品,背包容量为j-w ix i,以下说法正确的是( AB…