当前位置: 首页 > news >正文

Spring Boot全局异常处理

使用注解方式处理全局异常

  1. 使用 @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";}
}
  1. 适用于返回视图的请求,方法返回值如果不是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");}
}
  1. 也可以@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";}
}
  1. 控制器私有的异常处理器,优先级高于全局异常处理器

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注解方法不抛出异常才是正确的)

核心源码位置

  1. 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;}
  1. 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 &#xff08;RestControllerAdvice&#xff09; 配合 ExceptionHandler适用于返回数据的请求&#xff08;一般是RESTful接口规范下的JSON报文&#xff09;package com.example.exception;import org.slf4j.Logger; import org.s…...

websocket每隔5秒给服务端send一次信息

websocket轮询每隔5秒给服务端send一次信息&#xff0c;主要功能点如下&#xff1a;socket 采用了定时器 setInterval&#xff08;&#xff09; 需要清除定时器否则会报错监听了突然关闭浏览器窗口&#xff0c;destroyed里面直接监听 window.removeEventListener("beforeu…...

2023年中职网络安全——SQL注入测试(PL)解析

SQL注入测试(PL) 任务环境说明: 服务器场景:Server2312服务器场景操作系统:未知(关闭链接)已知靶机存在网站系统,使用Nmap工具扫描靶机端口,并将网站服务的端口号作为Flag(形式:Flag字符串)值提交。访问网站/admin/pinglun.asp页面,此页面存在SQL注入漏洞,使用排…...

利用蜜罐捕捉攻击实验(31)

预备知识 1、蜜罐的含义和作用 蜜罐(Honeypot)是一种在互联网上运行的计算机系统。它是专门为吸引并诱骗那些试图非法闯入他人计算机系统的人(如电脑黑客)而设计的&#xff0c;蜜罐系统是一个包含漏洞的诱骗系统&#xff0c;它通过模拟一个或多个易受攻击的主机&#xff…...

PyTorch深度学习实战 | 自然语言处理与强化学习

PyTorch是当前主流深度学习框架之一&#xff0c;其设计追求最少的封装、最直观的设计&#xff0c;其简洁优美的特性使得PyTorch代码更易理解&#xff0c;对新手非常友好。本文主要介绍深度学习领域中自然语言处理与强化学习部分。自然语言区别于计算机所使用的机器语言和程序语…...

测牛学堂:接口测试基础理论和工具的使用

接口测试流程总结 1 需求分析&#xff0c;看产品经理的需求文档 2 接口文档解析&#xff0c;开发编写的api接口文档 3 设计测试用例 4脚本开发 5 执行及缺陷跟踪 6 生成测试报告 7接口自动化持续集成 测试解析接口文档 接口文档&#xff0c;又称为api文档&#xff0c;是由后…...

定长内存池的实现

解决的是固定大小的内存申请释放需求&#xff1a; 性能达到极致不考虑内存碎片问题(统一使用自由链表管理还回来的空间) 为了避免命名污染&#xff0c;不要直接using namespace std;只展开常用的。 #include <iostream> using std::cout; using std::endl;申请空间时有…...

三更草堂springSecurity的学习

源码地址&#xff1a;学习springSecurity (gitee.com) git&#xff1a;https://gitee.com/misszyg/spring-security.git 一&#xff0c;认证流程 1&#xff0c;经过UsernamePasswordAuthenticationFilter &#xff08;1&#xff09;传入了用户的账号&#xff0c;密码 源码&a…...

【C语言】指针的深度理解(一)

前言 我们已经了解了指针的概念&#xff0c;一是指针变量是用来存放地址的&#xff0c;每个地址都对应着唯一的内存空间。二是指针的大小是固定的4或8个字节&#xff08;取决于操作系统&#xff0c;32位的占4个字节&#xff0c;64位的占8个字节&#xff09;。三是指针是有类型…...

Kafka最佳实践

前言 Kafka 最佳实践&#xff0c;涉及 典型使用场景Kafka 使用的最佳实践 Kafka 典型使用场景 Data Streaming Kafka 能够对接到 Spark、Flink、Flume 等多个主流的流数据处理技术。利用 Kafka 高吞吐量的特点&#xff0c;客户可以通过 Kafka 建立传输通道&#xff0c;把应…...

入门教程: 认识 React用于构建用户界面的 JavaScript 库

课前准备 我们将会在这个教程中开发一个小游戏。你可能并不打算做游戏开发,然后就直接跳过了这个教程——但是不妨尝试一下!你将在该教程中学到关于构建 React 应用的基础知识,掌握这些知识后,你将会对 React 有更加深刻的理解。 这篇教程分为以下几个部分: 环境准备是学…...

极紫外光源高次谐波发生腔不同区域真空度精密控制解决方案

摘要&#xff1a;在高次谐波发生器中一般包含两个不同真空区域&#xff0c;一个是1~100Torr绝压范围的气池内部的低真空区域&#xff0c;一个是高阶谐波光路上的绝压为0.001Pa量级的高真空区域。本文针对此两个区域的真空度控制提出了相应的解决方案&#xff0c;特别是详细介绍…...

「Vue面试题」在vue中为什么data属性是一个函数而不是一个对象

文章目录一、实例和组件定义data的区别二、组件data定义函数与对象的区别三、原理分析四、结论一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象&#xff0c;也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:"…...

如何使用 ChatGPT 编写 SQL JOIN 查询

通过清晰的示例和解释&#xff0c;本文展示了 ChatGPT 如何简化和简化创建复杂 MySQL 查询的过程&#xff0c;使用户更容易与数据库交互并检索他们需要的数据。无论您是初学者还是经验丰富的开发人员&#xff0c;本文都提供了有关如何利用 ChatGPT 来增强您的 MySQL 查询编写技…...

vue2+elementUI完成添加学生删除学生案列

效果图&#xff1a; 点击添加学生按钮&#xff0c;弹出Dialog,收集用户信息&#xff1a; el-table中自定义复选框&#xff0c;选中一行&#xff0c;可以点击删除 代码区域&#xff1a;就一个HTML文件 <!DOCTYPE html> <html lang"en"> <head>&…...

对void的深度理解

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; void前言一、 void 关键字二、 void修饰函数返回值和参数三、void指针3.1void * 定义的…...

哪款游戏蓝牙耳机好用?好用的游戏蓝牙耳机推荐

现在&#xff0c;不少人喜欢戴蓝牙耳机玩游戏&#xff0c;而在戴蓝牙耳机玩游戏时难免会产生音画不同步的问题。现在越来越多的蓝牙耳机支持游戏模式&#xff0c;那么&#xff0c;哪款游戏蓝牙耳机好用&#xff1f;接下来&#xff0c;我来给大家推荐几款好用的游戏蓝牙耳机&…...

求职(怎么才算精通JAVA开发)

在找工作的的时候,有时候我们需要对自己的技术水平做一个评估。特别是Java工程师,我们该怎么去表达自己的能力和正确认识自己所处的技术水平呢。技术一般的人,一般都不敢说自己精通JAVA,因为你说了精通JAVA几乎就给了面试官一个可以随便往死里问的理由了。很多不自信的一般…...

C++网络编程(三)IO复用

C网络编程(三)IO复用 前言 多进程/多线程网络服务端在创建进程/线程时&#xff0c;CPU和内存开销很大。因为多线程/进程并发模型&#xff0c;为每个socket分配一个线程/进程。而IO复用采用单个的进程/线程就可以管理多个socket。 select 系统调用原型&#xff1a; #includ…...

第十四届蓝桥杯(第三期)模拟赛试题与题解 C++

第十四届蓝桥杯&#xff08;第三期&#xff09;模拟赛试题与题解 C 试题 A 【问题描述】 请找到一个大于 2022 的最小数&#xff0c;这个数转换成十六进制之后&#xff0c;所有的数位&#xff08;不含前导 0&#xff09;都为字母&#xff08;A 到 F&#xff09;。  请将这个…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...