Spring Boot全局异常处理
使用注解方式处理全局异常
使用 @ControllerAdvice (@RestControllerAdvice) 配合 @ExceptionHandler
适用于返回数据的请求(一般是RESTful接口规范下的JSON报文)
package com.example.exception;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.example.exception.ControllerController.MyException1;@RestControllerAdvice
public class RestControllerExceptionAdvice {private static transient Logger logger = LoggerFactory.getLogger(RestControllerExceptionAdvice.class);@ExceptionHandler(MyException1.class)public String MyException1(MyException1 e) {logger.info("RestControllerExceptionAdvice MyException1 异常->{}", e.getMessage());return "error";}
}适用于返回视图的请求,方法返回值如果不是ModelAndView而是字符串时,字符串会被解析成视图名
package com.example.exception;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import com.example.exception.ControllerController.MyException1;
import com.example.exception.ControllerController.MyException3;@ControllerAdvice
public class ControllerExceptionAdvice {private static transient Logger logger = LoggerFactory.getLogger(ControllerExceptionAdvice.class);@ExceptionHandler(MyException1.class)public String uploadException(MyException1 e) {logger.info("ControllerExceptionAdvice MyException1 异常->{}", e.getMessage());return "this is a viewName1";}@ExceptionHandler(MyException3.class)public ModelAndView uploadException(MyException3 e) {logger.info("ControllerExceptionAdvice MyException3 异常->{}", e.getMessage());return new ModelAndView("this is a viewName3");}
}也可以@ControllerAdvice配合使用@ResponseBody完成和@RestControllerAdvice一样的效果(不建议这样使用,个人感觉会使代码很混乱)
package com.example.exception;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.example.exception.ControllerController.MyException1;
import com.example.exception.ControllerController.MyException2;
import com.example.exception.ControllerController.MyException3;@ControllerAdvice
public class ControllerExceptionAdvice {private static transient Logger logger = LoggerFactory.getLogger(ControllerExceptionAdvice.class);@ExceptionHandler(MyException2.class)@ResponseBodypublic String uploadException(MyException2 e) {logger.info("ControllerExceptionAdvice MyException2 异常->{}", e.getMessage());return "this is a rsp";}
}控制器私有的异常处理器,优先级高于全局异常处理器
package com.example.exception;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RestControllerController {private static transient Logger logger = LoggerFactory.getLogger(RestControllerController.class);@GetMapping("/test")@ResponseBodypublic String testExceptionResolver() {throw new RuntimeException("这是测试控制器异常");}// 只针对当前控制器,优先级高于全局异常处理器@ResponseBody@ExceptionHandler(RuntimeException.class)public String uploadException(RuntimeException e) {logger.info("RestControllerController RuntimeException 异常->{}", e.getMessage());return "error";}
}使用HandlerExceptionResolver接口实现处理全局异常
package com.example.exception;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;@Component
public class ExceptionResolver1 implements HandlerExceptionResolver {private static transient Logger logger = LoggerFactory.getLogger(ExceptionResolver1.class);@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {ModelAndView mv = new ModelAndView();logger.info("ExceptionResolver 异常->{}", ex.getMessage());return mv;}
}package com.example.exception;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;@Component
public class ExceptionResolver2 implements HandlerExceptionResolver {private static transient Logger logger = LoggerFactory.getLogger(ExceptionResolver1.class);@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {ModelAndView mv = new ModelAndView();logger.info("ExceptionResolver2 异常->{}", ex.getMessage());return mv;}
}HandlerExceptionResolver接口是没有排序的,所以应用程序无法保证执行顺序
如果第一个执行的HandlerExceptionResolver已经返回了ModelAndView,那么后续所有的HandlerExceptionResolver都将不会被执行;返回null会使得后续HandlerExceptionResolver继续执行,一直到返回ModelAndView的HandlerExceptionResolver结束(如果你的所有实现全部返回null值也没有意义)
@ControllerAdvice (@RestControllerAdvice) 配合 @ExceptionHandler的全局异常处理会先于HandlerExceptionResolver执行,所以@ExceptionHandler注解方法中的异常可以由HandlerExceptionResolver处理(保证@ExceptionHandler注解方法不抛出异常才是正确的)
核心源码位置
Spring MVC在 DispatcherServlet#processHandlerException()方法中使用上述的异常解析器,关于异常处理返回值解析的问题以及异常处理顺序的问题都可以以该方法为起点打断点DEBUG
@Nullableprotected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {// Success and error responses may use different content typesrequest.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);// Check registered HandlerExceptionResolvers...ModelAndView exMv = null;//就在这个位置if (this.handlerExceptionResolvers != null) {for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {if (exMv.isEmpty()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;}// We might still need view name translation for a plain error model...if (!exMv.hasView()) {String defaultViewName = getDefaultViewName(request);if (defaultViewName != null) {exMv.setViewName(defaultViewName);}}if (logger.isTraceEnabled()) {logger.trace("Using resolved error view: " + exMv, ex);}else if (logger.isDebugEnabled()) {logger.debug("Using resolved error view: " + exMv);}WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());return exMv;}throw ex;}Spring MVC在WebMvcConfigurationSupport#handlerExceptionResolver()方法中处理异常解析器的注入,该方法返回的是一个包装器HandlerExceptionResolverComposite,该包装器持有HandlerExceptionResolver
@Beanpublic HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();configureHandlerExceptionResolvers(exceptionResolvers);if (exceptionResolvers.isEmpty()) {addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);return composite;}WebMvcConfigurationSupport#configureHandlerExceptionResolvers()方法是一个空实现,需要子类实现去注入自定义的HandlerExceptionResolver
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}如果没有提供自定义的异常解析器,会默认使用WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers()方法加载异常解析器
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,ContentNegotiationManager mvcContentNegotiationManager) {ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);exceptionHandlerResolver.setMessageConverters(getMessageConverters());exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}if (this.applicationContext != null) {exceptionHandlerResolver.setApplicationContext(this.applicationContext);}exceptionHandlerResolver.afterPropertiesSet();exceptionResolvers.add(exceptionHandlerResolver);ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);exceptionResolvers.add(responseStatusResolver);exceptionResolvers.add(new DefaultHandlerExceptionResolver());}ExceptionHandlerExceptionResolver类的exceptionHandlerAdviceCache属性中存储的就是@ExceptionHandler注解的异常处理器,初始化核心代码
@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beansinitExceptionHandlerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}private void initExceptionHandlerAdviceCache() {if (getApplicationContext() == null) {return;}List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();if (beanType == null) {throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);}ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);if (resolver.hasExceptionMappings()) {this.exceptionHandlerAdviceCache.put(adviceBean, resolver);}if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {this.responseBodyAdvice.add(adviceBean);}}if (logger.isDebugEnabled()) {int handlerSize = this.exceptionHandlerAdviceCache.size();int adviceSize = this.responseBodyAdvice.size();if (handlerSize == 0 && adviceSize == 0) {logger.debug("ControllerAdvice beans: none");}else {logger.debug("ControllerAdvice beans: " +handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");}}}注意:自定义实现WebMvcConfigurationSupport可能会导致 @ExceptionHandler异常处理器失效,比如重写WebMvcConfigurationSupport#configureHandlerExceptionResolvers()方法并添加了自己的异常处理器
package com.example.config;import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;@Configuration
public class WebMvcConfigurationSupportTest extends WebMvcConfigurationSupport {@Overrideprotected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {exceptionResolvers.add(new HandlerExceptionResolver() {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {System.out.println("resolveException.......................");return new ModelAndView();}});}
}
相关文章:
Spring Boot全局异常处理
使用注解方式处理全局异常使用 ControllerAdvice (RestControllerAdvice) 配合 ExceptionHandler适用于返回数据的请求(一般是RESTful接口规范下的JSON报文)package com.example.exception;import org.slf4j.Logger; import org.s…...
websocket每隔5秒给服务端send一次信息
websocket轮询每隔5秒给服务端send一次信息,主要功能点如下:socket 采用了定时器 setInterval() 需要清除定时器否则会报错监听了突然关闭浏览器窗口,destroyed里面直接监听 window.removeEventListener("beforeu…...
2023年中职网络安全——SQL注入测试(PL)解析
SQL注入测试(PL) 任务环境说明: 服务器场景:Server2312服务器场景操作系统:未知(关闭链接)已知靶机存在网站系统,使用Nmap工具扫描靶机端口,并将网站服务的端口号作为Flag(形式:Flag字符串)值提交。访问网站/admin/pinglun.asp页面,此页面存在SQL注入漏洞,使用排…...
利用蜜罐捕捉攻击实验(31)
预备知识 1、蜜罐的含义和作用 蜜罐(Honeypot)是一种在互联网上运行的计算机系统。它是专门为吸引并诱骗那些试图非法闯入他人计算机系统的人(如电脑黑客)而设计的,蜜罐系统是一个包含漏洞的诱骗系统,它通过模拟一个或多个易受攻击的主机ÿ…...
PyTorch深度学习实战 | 自然语言处理与强化学习
PyTorch是当前主流深度学习框架之一,其设计追求最少的封装、最直观的设计,其简洁优美的特性使得PyTorch代码更易理解,对新手非常友好。本文主要介绍深度学习领域中自然语言处理与强化学习部分。自然语言区别于计算机所使用的机器语言和程序语…...
测牛学堂:接口测试基础理论和工具的使用
接口测试流程总结 1 需求分析,看产品经理的需求文档 2 接口文档解析,开发编写的api接口文档 3 设计测试用例 4脚本开发 5 执行及缺陷跟踪 6 生成测试报告 7接口自动化持续集成 测试解析接口文档 接口文档,又称为api文档,是由后…...
定长内存池的实现
解决的是固定大小的内存申请释放需求: 性能达到极致不考虑内存碎片问题(统一使用自由链表管理还回来的空间) 为了避免命名污染,不要直接using namespace std;只展开常用的。 #include <iostream> using std::cout; using std::endl;申请空间时有…...
三更草堂springSecurity的学习
源码地址:学习springSecurity (gitee.com) git:https://gitee.com/misszyg/spring-security.git 一,认证流程 1,经过UsernamePasswordAuthenticationFilter (1)传入了用户的账号,密码 源码&a…...
【C语言】指针的深度理解(一)
前言 我们已经了解了指针的概念,一是指针变量是用来存放地址的,每个地址都对应着唯一的内存空间。二是指针的大小是固定的4或8个字节(取决于操作系统,32位的占4个字节,64位的占8个字节)。三是指针是有类型…...
Kafka最佳实践
前言 Kafka 最佳实践,涉及 典型使用场景Kafka 使用的最佳实践 Kafka 典型使用场景 Data Streaming Kafka 能够对接到 Spark、Flink、Flume 等多个主流的流数据处理技术。利用 Kafka 高吞吐量的特点,客户可以通过 Kafka 建立传输通道,把应…...
入门教程: 认识 React用于构建用户界面的 JavaScript 库
课前准备 我们将会在这个教程中开发一个小游戏。你可能并不打算做游戏开发,然后就直接跳过了这个教程——但是不妨尝试一下!你将在该教程中学到关于构建 React 应用的基础知识,掌握这些知识后,你将会对 React 有更加深刻的理解。 这篇教程分为以下几个部分: 环境准备是学…...
极紫外光源高次谐波发生腔不同区域真空度精密控制解决方案
摘要:在高次谐波发生器中一般包含两个不同真空区域,一个是1~100Torr绝压范围的气池内部的低真空区域,一个是高阶谐波光路上的绝压为0.001Pa量级的高真空区域。本文针对此两个区域的真空度控制提出了相应的解决方案,特别是详细介绍…...
「Vue面试题」在vue中为什么data属性是一个函数而不是一个对象
文章目录一、实例和组件定义data的区别二、组件data定义函数与对象的区别三、原理分析四、结论一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象,也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:"…...
如何使用 ChatGPT 编写 SQL JOIN 查询
通过清晰的示例和解释,本文展示了 ChatGPT 如何简化和简化创建复杂 MySQL 查询的过程,使用户更容易与数据库交互并检索他们需要的数据。无论您是初学者还是经验丰富的开发人员,本文都提供了有关如何利用 ChatGPT 来增强您的 MySQL 查询编写技…...
vue2+elementUI完成添加学生删除学生案列
效果图: 点击添加学生按钮,弹出Dialog,收集用户信息: el-table中自定义复选框,选中一行,可以点击删除 代码区域:就一个HTML文件 <!DOCTYPE html> <html lang"en"> <head>&…...
对void的深度理解
作者:小树苗渴望变成参天大树 作者宣言:认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! void前言一、 void 关键字二、 void修饰函数返回值和参数三、void指针3.1void * 定义的…...
哪款游戏蓝牙耳机好用?好用的游戏蓝牙耳机推荐
现在,不少人喜欢戴蓝牙耳机玩游戏,而在戴蓝牙耳机玩游戏时难免会产生音画不同步的问题。现在越来越多的蓝牙耳机支持游戏模式,那么,哪款游戏蓝牙耳机好用?接下来,我来给大家推荐几款好用的游戏蓝牙耳机&…...
求职(怎么才算精通JAVA开发)
在找工作的的时候,有时候我们需要对自己的技术水平做一个评估。特别是Java工程师,我们该怎么去表达自己的能力和正确认识自己所处的技术水平呢。技术一般的人,一般都不敢说自己精通JAVA,因为你说了精通JAVA几乎就给了面试官一个可以随便往死里问的理由了。很多不自信的一般…...
C++网络编程(三)IO复用
C网络编程(三)IO复用 前言 多进程/多线程网络服务端在创建进程/线程时,CPU和内存开销很大。因为多线程/进程并发模型,为每个socket分配一个线程/进程。而IO复用采用单个的进程/线程就可以管理多个socket。 select 系统调用原型: #includ…...
第十四届蓝桥杯(第三期)模拟赛试题与题解 C++
第十四届蓝桥杯(第三期)模拟赛试题与题解 C 试题 A 【问题描述】 请找到一个大于 2022 的最小数,这个数转换成十六进制之后,所有的数位(不含前导 0)都为字母(A 到 F)。 请将这个…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
