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

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

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


文章目录

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


1. 统一异常处理

1.1 使用

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

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

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

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

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

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {@ExceptionHandlerpublic Object handleException(Exception e) {//表示代码如果出现Exception(及其之类)异常,就返回Result的对象return Result.fail(e.getMessage());}@ExceptionHandlerpublic Object handleException(ArithmeticException e) {return Result.fail("算术异常" + e.getMessage());}@ExceptionHandlerpublic 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注解的BeanList<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.lryc.cn/news/414977.html

相关文章:

  • 用Manim实现——计算和绘制图形下方区域
  • MySQL 保姆级教程(十五): 组合查询
  • 《动手做科研》06. 如何产生新的研究想法
  • 【Kubernetes】Deployment 的状态
  • 新手学习Gazebo+ros仿真控制小车-----易错和自己理解
  • jdbc(mysql)
  • 【Linux】搜索log在哪个文件中执行的方法
  • web小游戏开发:2048(完)移动操作及动画效果
  • Redis学习笔记——第20章 Lua脚本
  • MySQL--日志管理
  • 【Nuxt】内置组件和全局样式使用
  • Java中spring boot validation 自定义注解使用
  • Android笔试面试题AI答之广播(1)
  • 微软商店无法加载,检查你的连接-解决方案
  • 数据结构实验报告-树与二叉树
  • 基于Django+MySQL球馆场地预约系统的设计与实现(源码+论文+部署讲解等)
  • 8 MQTT
  • 【文件系统】抽象磁盘的存储结构 CHS寻址法 | sector数组 | LAB数组
  • 基于python旅游推荐系统(源码+论文+部署讲解等)
  • Mysql大单表JSON优化
  • 电脑开机启动项管理小工具,绿色免安装
  • 一例AutoHotkey语言生成的文件夹病毒分析
  • 【机器学习第7章——贝叶斯分类器】
  • C++ QT开发 学习笔记(3)
  • 【Python实战】如何优雅地实现文字 二维码检测?
  • 行为型设计模式3:模板方法/备忘录/解释器/迭代器
  • 思源笔记软件的优缺点分析
  • 追问试面试系列:Dubbo
  • 动手学深度学习V2每日笔记(卷积层)
  • qcom ucsi probe