Spring - 解析 统一数据格式返回以及统一异常处理

news2025/1/20 22:44:21

接上篇文章的统一数据格式返回…


文章目录

    • 1. 统一异常处理
      • 1.1 使用
    • 2. 统一数据返回和统一异处理是怎么实现的
      • 2.1 `initHandleAdapters`
      • 2.2 `initHandleExceptionResolvers`


1. 统一异常处理

1.1 使用

统一异常处理的两个关键的注解是@ControllerAdvice + @ExceptionHandler

  • @ControllerAdvice 表示控制器通知类
  • @ExceptionHandler:是异常处理器

两者结合就表示:出现异常的时候执行某个通知,也就是执行某个方法具体的代码如下:

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
    @ExceptionHandler
    public Object handleException(Exception e) {
        //表示代码如果出现Exception(及其之类)异常,就返回Result的对象
        return Result.fail(e.getMessage());
    }
}

其中Result是自定义的放回结果类:
image.png
同时,我们可以针对不同的异常,返回不同的结果

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
    @ExceptionHandler
    public Object handleException(Exception e) {
        //表示代码如果出现Exception(及其之类)异常,就返回Result的对象
        return Result.fail(e.getMessage());
    }

    @ExceptionHandler
    public Object handleException(ArithmeticException e) {
        return Result.fail("算术异常" + e.getMessage());
    }


    @ExceptionHandler
    public Object handleException(NullPointerException e) {
        return Result.fail("空指针异常" + e.getMessage());
    }
}

测试:

@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/Test1")
    public String Test1() {
        int a = 10 / 0;
        return "t1";
    }


    @RequestMapping("Test2")
    public String Test2() {
        int[] arr = new int[4];
        int b = arr[4];
        return "t2";
    }
}

image.png
image.png

2. 统一数据返回和统一异处理是怎么实现的

统一数据返回和统一异处理是怎么实现的?
我们从DispatcherServlet的代码开始分析
当Tomcat启动的时候,有一个核心的类DispatcherServlet,用来控制程序的执行顺序
这个对象在创建的时候,会初始化一系列的对象:
image.png
其中与统一数据返回和统一异常处理相关的就是initHandleAdaptersinitHandleExceptionResolvers

2.1 initHandleAdapters

这个⽅法在执时会查找使用所有的 @ControllerAdvice 类,把 ResponseBodyAdvice
放在容器中,当发⽣某个事件时,调⽤相应的Advice⽅法,⽐如返回数据前调⽤统⼀数据封装

private void initControllerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}

    //查找所有被@ControllerAdvice注解的Bean
		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

		List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
			Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
			}
			Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(adviceBean, binderMethods);
			}

            //遍历adviceBeans列表,检查每个ControllerAdviceBean对应的bean类型是否实现了RequestBodyAdvice或ResponseBodyAdvice接口
			if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				requestResponseBodyAdviceBeans.add(adviceBean);
			}
		}

		if (!requestResponseBodyAdviceBeans.isEmpty()) {
			this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
		}

		if (logger.isDebugEnabled()) {
			int modelSize = this.modelAttributeAdviceCache.size();
			int binderSize = this.initBinderAdviceCache.size();
			int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
			int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
			if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
				logger.debug("ControllerAdvice beans: none");
			}
			else {
				logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
						" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
			}
		}
	}

2.2 initHandleExceptionResolvers

initHandleExceptionResolvers方法会取得所有实现了HandleExceeptionResolver接口的Bean,其中就有一个ExceptionHandlerExceptionResolver 的bean,这个Bean在应用启动的时候或获取到所有被注解@ControllerAdvice标注的bean对象,做进一步处理
image.png
当Controller抛出异常的时候,DispatcherServlet使用ExceptionHandlerExceptionResolver 来解析异常,而ExceptionHandlerExceptionResolver 通过ExceptionHandlerMethodResolver 来解析异常

@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
    List<Class<? extends Throwable>> matches = new ArrayList<>();
    for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
        if (mappedException.isAssignableFrom(exceptionType)) {
            matches.add(mappedException);
        }
    }
    if (!matches.isEmpty()) {
        if (matches.size() > 1) {
            matches.sort(new ExceptionDepthComparator(exceptionType));
        }
        return this.mappedMethods.get(matches.get(0));
    }
    else {
        return NO_MATCHING_EXCEPTION_HANDLER_METHOD;
    }
}

我们通过调试来看处理异常的整个过程
image.png

断点打在这个地方(第一次执行到的时候不是我们想要的,按f9会再次跳到这个地方第二次执行)
image.png

往下执行,就会发现此时正在遍历mappedMethods,里面就是我们在ErrorAdvice里面指定的所有异常
image.png

遍历完后,实际上就是筛选出哪些异常能够处理我们当前的异常类型,由于我们当前是算术异常,那么能匹配的就留下了两个
image.png

调用sort方法之后,就会按照优先级排序好,以便我们更精确的处理异常
image.png

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

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

相关文章

C++入门基本语法(2)

一、引用 1、基本概念与定义 引用不是新定义一个变量&#xff0c;而是给已存在的变量起一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它所引用的变量公用同一块内存空间&#xff1b; 引用的写法&#xff1a;变量类型& 引用别名 变量&#xff…

第六周:机器学习

目录 摘要 Abstract 一、深度学习的优化算法 1、SGD 2、SGDM 3、Adagrad 4、RMSProp 5、Adam算法 二、分类器 三、卷积神经网络 总结 摘要 接着上周学习率在训练中的影响&#xff0c;本周对深度学习常见的几种优化算法做了总结&#xff0c;着重分析Adam算法的优缺…

太阳光强光照射实验在材料科学中的应用

强光照射实验方法 所谓的强光照射即使用人造太阳光模拟器设备模拟太阳光的真实光照环境。强光照射实验是一种在材料科学中常用的实验方法&#xff0c;主要用于研究材料在强烈光照条件下的稳定性、性能变化及其内在机制。实验通常涉及将材料置于特定波长和强度的光源下&#xff…

【vulnhub】DerpNStink靶机

靶机安装 下载地址&#xff1a;DerpNStink: 1 ~ VulnHub 信息收集 靶机IP扫描 nmap 192.168.93.0/24 端口扫描&#xff0c;开放21、22、80端口 nmap -A 192.168.93.158 -p- 目录扫描 dirsearch -u http://192.168.93.158 进行网址访问&#xff0c;页面上只有个单词DeRPn…

IJCAI 2024 | 时空数据(Spatial-Temporal)论文总结

2024 IJCAI&#xff08;International Joint Conference on Artificial Intelligence, 国际人工智能联合会议&#xff09;在2024年8月3日-9日在韩国济州岛举行。 本文总结了IJCAI2024有关时空数据(Spatial-temporal) 的相关论文&#xff0c;如有疏漏&#xff0c;欢迎大家补充。…

【微服务】springboot 整合 SA-Token 使用详解

目录 一、前言 二、认证与授权介绍 2.1 什么是认证 2.1.1 认证的目的 2.1.2 认证基本步骤 2.2 什么是授权 2.2.1 常用的授权模型 三、微服务中常用的认证安全框架 3.1 Spring Security 3.1.1 Spring Security 特点 3.2 JWT (JSON Web Tokens) 3.2.1 JWT特点 3.3 其…

鸿蒙笔记--Socket

这一节主要了解鸿蒙Socket通信&#xff0c;在鸿蒙系统中&#xff0c;Socket TCP通讯是一种常用的网络通信方式&#xff0c;它提供了可靠的、面向连接的数据传输服务。它主要用到ohos.net.socket这个库&#xff1b; 栗子: export default class SocketUtils {public static c…

跟《经济学人》学英文:2024年08月03日这期 How deep is Britain’s fiscal “black hole”?

How deep is Britain’s fiscal “black hole”? Rachel Reeves sets out her first big decisions as chancellor set out: 陈述&#xff0c;阐明 “Set out” 的意思是陈述、阐明或展示。在这个上下文中&#xff0c;指的是Rachel Reeves作为财政大臣阐明了她的第一个重要决…

数据结构(8):排序

1 排序的基本概念 稳定性&#xff01;&#xff01;&#xff01; 分类[内部、外部] 机械硬盘的读取是很慢的&#xff01;&#xff01;&#xff01; 总结 2 内部排序 2.1 插入排序 前面时 保存好的 是排好序的哦 一个一个检查&#xff01;然后放到改在的位置 只有小的时候换&am…

AI论文速读 | 2024MM【开源】时间序列预测中频率动态融合

&#x1f4f0;题目&#xff1a;Pre-Training Identification of Graph Winning Tickets in Adaptive Spatial-Temporal Graph Neural Networks &#x1f58b;作者&#xff1a;Xingyu Zhang&#xff08;张星宇&#xff09;, Siyu Zhao&#xff08;赵思雨&#xff09;, Zeen Song…

200kg大载重履带遥控无人车技术详解

200kg大载重履带遥控无人车&#xff08;以下简称“无人车”&#xff09;是一种专为复杂地形和高负载运输设计的智能化车辆。它集成了先进的动力系统、高效的行走机构、灵活的操控系统以及强大的负载能力&#xff0c;能够在人难以到达或危险的环境中执行物资运输、勘探探测、应急…

【八股文】网络基础

1.简述一下TCP和UDP的区别&#xff1f; 特性TCP&#xff08;Transmission Control Protocol&#xff09;UDP&#xff08;User Datagram Protocol&#xff09;连接类型面向连接&#xff0c;需要建立三次握手连接无连接&#xff0c;发送数据无需建立连接数据传输提供可靠的数据传…

5款免费写作神器,高效率自动生成文章

在当今数字化的时代&#xff0c;写作变得越来越重要&#xff0c;而各种免费的写作神器也应运而生&#xff0c;为创作者们提供了极大的便利。以下是本文为大家详细介绍5个免费写作神器&#xff0c;帮助大家提高写作效率&#xff0c;让创作不再烦恼。 1、火呱ai写作 这是一款综合…

MySQL事务,锁,MVCC总结

mysql中最重要的就是事务&#xff0c;其四大特性让我们维持了数据的平衡&#xff0c;一致。那么事务究竟是什么&#xff0c;与什么相关&#xff0c;他的使用步骤&#xff0c;以及使用过程中我们会遇到什么问题呢&#xff1f;下面我们一起学习交流! 1.MySQL的存储引擎&#xff…

Redis学习[7] ——如何使用Redis实现分布式锁?

如何用Redis实现分布式锁&#xff1f; 9.5.1 为什么Redis能用来实现分布式锁&#xff1f; 分布式锁是用于分布式环境下并发控制的一种机制&#xff0c;用于控制某个资源在同一时刻只能被一个应用所使用。如下图所示&#xff1a; Redis可以被多个客户端共享访问&#xff0c;可…

jenkins自动化构建docker镜像并上传至harbor仓库

1、插件下载 首先进入jenkins之后需要现在“Maven”、“GitLab”、“Jdk”、“SSH”、“Git”的相关插件&#xff0c;这里不再赘述&#xff0c;需要什么插件直接安装即可 搜索对应插件后选择直接安装即可 2、系统全局配置 2.1 Maven配置 配置maven安装的相应的setting文件 …

How does age change how you learn?(2)年龄如何影响学习能力?(二)

Do different people experience decline differently? 不同人经历的认知衰退会有不同吗? Do all people experience cognitive decline uniformly?Or do some people’s minds slip while others stay sharp much longer? 所有人经历的认知衰退都是一样的吗?还是有些人…

计时器(Python)

代码 import time from tkinter import ttk import threading from tkinter import scrolledtext import tkinter as tkclass TimerApp:def __init__(self, root):self.root rootself.root.title("计时器")self.screen_w, self.screen_h self.root.winfo_screenwi…

Conditional Detr

encoder和detr相同&#xff0c;只修改了decoder部分。

(STM32笔记)九、RCC时钟树与时钟 第一部分

我用的是正点的STM32F103来进行学习&#xff0c;板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话&#xff0c;用的也是这个板子和教程。 九、RCC时钟树与时钟 九、RCC时钟树与时钟1、时钟树HSE时钟HSI时钟锁相环时钟系统时钟HCLK时钟PCLK1时钟PCLK2时钟RTC时钟独…