企业实战之spring增强器实现《全局异常处理器》

2023-10-21 21:20

本文主要是介绍企业实战之spring增强器实现《全局异常处理器》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

之前我们分享了对于spring项目的controller层,我们该如何简写我们的代码逻辑,所谓的简写就是 简化日志打印、参数校验、异常捕获和响应结果的封装这几个步骤,让我们把更多的时间留给我们更关注的业务逻辑,这里给一个之前的文章链接,读了过后可能你会更好的理解该篇文章的意义《Api写法第三篇》。
废话不多说了,我们看一下我们今天的主角儿,@ControllerAdvice注解,这个是spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强。今天我们就是使用它,来全局捕获系统中的异常,并对接口返回的的HTTP状态码做控制,下面来看代码的实现

代码实现

package com.zhuma.demo.handler;import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;import com.zhuma.demo.comm.handler.BaseAggregationLayerGlobalExceptionHandler;
import com.zhuma.demo.comm.result.Result;
import com.zhuma.demo.exception.BusinessException;/*** @desc 统一异常处理器* * @author zhumaer* @since 8/31/2017 3:00 PM*/
@RestController
@ControllerAdvice
public class GlobalExceptionHandler extends BaseAggregationLayerGlobalExceptionHandler {/* 处理400类异常 */@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(ConstraintViolationException.class)public Result handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {return super.handleConstraintViolationException(e, request);}@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(HttpMessageNotReadableException.class)public Result handleConstraintViolationException(HttpMessageNotReadableException e, HttpServletRequest request) {return super.handleConstraintViolationException(e, request);}@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(BindException.class)public Result handleBindException(BindException e, HttpServletRequest request) {return super.handleBindException(e, request);}@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(MethodArgumentNotValidException.class)public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {return super.handleMethodArgumentNotValidException(e, request);}/* 处理自定义异常 */@ExceptionHandler(BusinessException.class)public ResponseEntity<Result> handleBusinessException(BusinessException e, HttpServletRequest request) {return super.handleBusinessException(e, request);}/* 处理运行时异常 */@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)@ExceptionHandler(RuntimeException.class)public Result handleRuntimeException(RuntimeException e, HttpServletRequest request) {return super.handleRuntimeException(e, request);}}

解释说明

  • 这里我们使用@ControllerAdvice注解,如果是在写resetful风格的接口时,别忘记它是跟@RestController联用的哦
  • 这个类只是我们的定义,真正的实现逻辑是在其继承BaseAggregationLayerGlobalExceptionHandler 类中的,这样做的目的是因为在微服务系统中很多逻辑是可以共用的,所以向上抽离下代码还是很有必要的。
  • 目前处理了简单的参数错误异常(HTTP状态码:400)、运行时异常(HTTP状态码:500)和自定义异常(HTTP状态码:根据情况定)这几大类,其中自定义业务异常会细化我们在系统中常用到的情况,例如:数据找不到时,会抛出DataNotFoundException返回HTTP状态码404,没有权限时,会抛出PermissionForbiddenException异常返回401状态码,数据已存在也就是数据冲突会抛出DataConflictException返回409等等的异常,所有的自定义异常都会继承我们BusinessException业务异常,后面我会写一个关于自定义异常的文章,到时我们在更加详细的说明其使用。

统一异常处理实现类:

package com.zhuma.demo.comm.handler;import java.util.List;import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;import com.zhuma.demo.comm.result.ParameterInvalidItem;
import com.zhuma.demo.comm.result.Result;
import com.zhuma.demo.enums.ExceptionEnum;
import com.zhuma.demo.enums.ResultCode;
import com.zhuma.demo.exception.BusinessException;
import com.zhuma.demo.util.ConvertUtil;/*** @desc 聚合层全局异常处理类* * @author zhumaer* @since 10/10/2017 9:54 AM*/
public class BaseAggregationLayerGlobalExceptionHandler {private static final Logger LOGGER = LoggerFactory.getLogger(BaseAggregationLayerGlobalExceptionHandler.class);/*** 违反约束异常*/protected Result handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {LOGGER.info("handleConstraintViolationException start, uri:{}, caused by: ", request.getRequestURI(), e);List<ParameterInvalidItem> parameterInvalidItemList = ConvertUtil.convertCVSetToParameterInvalidItemList(e.getConstraintViolations());return Result.failure(ResultCode.PARAM_IS_INVALID, parameterInvalidItemList);}/*** 处理验证参数封装错误时异常*/protected Result handleConstraintViolationException(HttpMessageNotReadableException e, HttpServletRequest request) {LOGGER.info("handleConstraintViolationException start, uri:{}, caused by: ", request.getRequestURI(), e);return Result.failure(ResultCode.PARAM_IS_INVALID, e.getMessage());}/*** 处理参数绑定时异常(反400错误码)*/protected Result handleBindException(BindException e, HttpServletRequest request) {LOGGER.info("handleBindException start, uri:{}, caused by: ", request.getRequestURI(), e);List<ParameterInvalidItem> parameterInvalidItemList = ConvertUtil.convertBindingResultToMapParameterInvalidItemList(e.getBindingResult());return Result.failure(ResultCode.PARAM_IS_INVALID, parameterInvalidItemList);}/*** 处理使用@Validated注解时,参数验证错误异常(反400错误码)*/protected Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {LOGGER.info("handleMethodArgumentNotValidException start, uri:{}, caused by: ", request.getRequestURI(), e);List<ParameterInvalidItem> parameterInvalidItemList = ConvertUtil.convertBindingResultToMapParameterInvalidItemList(e.getBindingResult());return Result.failure(ResultCode.PARAM_IS_INVALID, parameterInvalidItemList);}/*** 处理通用自定义业务异常*/protected ResponseEntity<Result> handleBusinessException(BusinessException e, HttpServletRequest request) {LOGGER.info("handleBusinessException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());ExceptionEnum ee = ExceptionEnum.getByEClass(e.getClass());if (ee != null) {return ResponseEntity.status(ee.getHttpStatus()).body(Result.failure(ee.getResultCode(), e.getData()));}return ResponseEntity.status(HttpStatus.OK).body(e.getResultCode() == null ? Result.failure(e.getMessage()) : Result.failure(e.getResultCode(), e.getData()));}/*** 处理运行时系统异常(反500错误码)*/protected Result handleRuntimeException(RuntimeException e, HttpServletRequest request) {LOGGER.error("handleRuntimeException start, uri:{}, caused by: ", request.getRequestURI(), e);//TODO 可通过邮件、微信公众号等方式发送信息至开发人员、记录存档等操作return Result.failure(ResultCode.SYSTEM_INNER_ERROR);}}

解释说明

  • 上面的代码如果你粘贴至本地是会有错误的,因为我们并没有将代码完全贴出,这里只是给你提供一个异常处理的思路,如果你想要代码具体实现的话,可以关注文章下面公众号,或私聊我进行获取。
  • 在处理RuntimeException 时,我们可通过邮件、微信公众号、短信等等方式发送信息至开发人员并记录存档等操作,这对线上的排查错误很是有帮助的(当然如果你们有自己的监控系统或使用开源的cat系统可以在此处做埋点处理)。
  • 也许你会注意到我们的异常实现类叫聚合层全局异常处理类,这是因为我们使用的是微服务架构,服务会分为 网关层、聚合层、原子层,对于异常可能每个层次的处理逻辑会有一些不同,这里我们给了最常用的处理业务聚合层的异常处理逻辑,如果你正在研究微服务架构或苦恼不知道该怎么入手,也可以私聊我O(∩_∩)O~。

全局异常处理注意事项

  • 要根据不同的异常返回合适的HTTP状态码
  • 异常结果返回时,不要将详细的异常堆栈信息由接口返回给前端(这可能会暴漏一些敏感信息,造成安全隐患,再者说前端开发根本不会关心这些)
  • 约定HTTP状态码为500时,都是后端程序上的错误,一定要修复掉
  • 对于系统的错误,一定要详细的打印好日志,要将异常的详细堆栈上下文信息都打印出来(有的同学只打印e.getMessage()当问题出现时,就变得很尴尬,难以一眼定位问题),方便线上排查问题

最后

若上述文字有描述不清楚,或者你有哪里不太懂的可以评论留言或者关注我的公众号,公众号里也会不定期的发送一些关于企业实战相关的文章哈!

源码github地址:https://github.com/zhumaer/zhuma
QQ群号:629446754(欢迎加群)

欢迎关注我们的公众号或加群,等你哦!

这篇关于企业实战之spring增强器实现《全局异常处理器》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/257044

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

Mac系统下卸载JAVA和JDK的步骤

《Mac系统下卸载JAVA和JDK的步骤》JDK是Java语言的软件开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源,:本文主要介绍Mac系统下卸载JAVA和JDK的相关资料,需... 目录1. 卸载系统自带的 Java 版本检查当前 Java 版本通过命令卸载系统 Java2. 卸载自定

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Java Spring ApplicationEvent 代码示例解析

《JavaSpringApplicationEvent代码示例解析》本文解析了Spring事件机制,涵盖核心概念(发布-订阅/观察者模式)、代码实现(事件定义、发布、监听)及高级应用(异步处理、... 目录一、Spring 事件机制核心概念1. 事件驱动架构模型2. 核心组件二、代码示例解析1. 事件定义

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3