效果演示

我们先来看一下Springboot的默认效果

浏览器访问

客户端访问

划重点!!!

但是绝大部分公司的代码,都是没做自适应处理的,很大一部分原因在于,你在网上搜索Springboot全局异常处理,都是搜索到这么一段代码!

@ControllerAdvice
public class MyControllerAdvice {@ResponseBody@ExceptionHandler(value = Exception.class)public ResponseEntity<?> errorHandler(Exception ex) {// 处理异常}}

强烈建议先用自己常用的搜索引擎搜索一遍,然后再看一下自己公司代码,看看是不是类似这么一段代码再往下看。

当然很多同学可能会说,我们就已经和客户端约定很好了,只会有json,不会有返回html的场景。所以,不做这个适应,其实也是没问题的。但是如果你是做基础架构的同学,这个功能你是必须要做的,因为你对接的是整个公司的业务部门,Springboot能做,你做类似的基础组件,如果功能比Springboot还差,你让业务方的同学怎么想?

当然,对于绝大部分同学来说,不做问题也不大。

但是这样你会错过一个很好的学习机会。什么学习机会?因为很多同学平时总说,面试造火箭,工作中遇到不懂的问题百度或者谷歌一下就好了。然而,这个问题,你就没这么好搜索到。也就是说,绝大部分人都是面向搜索引擎编程,当遇到搜索引擎无法解决的问题的时候,就是体现你价值的时候,好好珍惜。

做不做这个功能我觉得不重要,这个宝贵的锻炼解决问题能力的机会是真的很难得,毕竟,确实大部分功能是真的简单搜索,或者肥朝交流群问问就能解决。

自适应原理

很多同学说,既然搜索不到,那果断一波源码走起。但是,Springboot源码这么多,请问哪里入手?这个才是重点!这个时候,我们可以官方文档走一波。

27.1.9 Error Handling

Spring Boot provides an /error mapping by default that handles all errors in a sensible way, and it is registered as a ‘global’ error page in the servlet container. For machine clients it will produce a JSON response with details of the error, the HTTP status and the exception message. For browser clients there is a ‘whitelabel’ error view that renders the same data in HTML format (to customize it just add a View that resolves to ‘error’). To replace the default behaviour completely you can implement ErrorController and register a bean definition of that type, or simply add a bean of type ErrorAttributes to use the existing mechanism but replace the contents.

一些同学说,英文看不懂?我挑5个重点单词给你,都是小学单词,只要小学能毕业,我认为都能看懂。

clients JSON browser HTML ErrorController

肥朝小声逼逼:这里特别强调,并不是说看官方文档是最优解决问题方案。还是那句话,老司机都是看菜吃饭的,解决问题的套路有很多,时间有限,我就不把所有套路一个一个列出来(其实是怕套路全部告诉你们了,你们就取关了!),直入主题就行。

从文档和小学的英文单词我们把目标锁定在了ErrorController,给大家看一下关键代码

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {@RequestMapping(produces = "text/html")public ModelAndView errorHtml(HttpServletRequest request,HttpServletResponse response) {HttpStatus status = getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));response.setStatus(status.value());ModelAndView modelAndView = resolveErrorView(request, response, status, model);return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);}@RequestMapping@ResponseBodypublic ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.ALL));HttpStatus status = getStatus(request);return new ResponseEntity<Map<String, Object>>(body, status);}}

从这里我们大致可以猜测出,要做一个自适应的全局异常处理,理论上是要这么写的。

@ControllerAdvice
public class MyExceptionHandler  {@ExceptionHandler(Exception.class)public String handleExceptionHtml(Exception e, HttpServletRequest httpServletRequest) {// 这里做一些你自己的处理,比如httpServletRequest.setAttribute("欢迎关注微信公众号","肥朝");return "forward:/error";}}

果然调试一波,发现果真如此。当然具体怎么自定义这个错误界面之类的,网上一搜就有,所以这些不是肥朝的重点。那么这个自适应全局异常似乎美滋滋了?

遇到问题

我们知道了这个自适应的全局异常处理的原理,也很容易想到怎么弄出bug。比如,你在拦截器出现了异常的话。

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new HandlerInterceptor() {@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {throw new RuntimeException("这里假装抛出一个肥朝异常");//return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}});}}

那么就会出现,StackOverflowError

因为拦截器出现异常,会掉进你的全局异常处理,然后你的全局异常处理,又进行forward,又进入了拦截器,然后一直循环。

那么怎么解决这个问题呢?我们见招拆招,这个时候,我要演示常见的几种不优雅,但是平时大家都容易做的写法。

将配置写死

registry.addInterceptor(new HandlerInterceptor() {@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {throw new RuntimeException("这里假装抛出一个肥朝异常");//return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}
}).excludePathPatterns("/error");

我们从

@RequestMapping("${server.error.path:${error.path:/error}}")

这里得知,这个error.path是可以配置的,很多同学图快,excludePathPatterns处写死了/error,这样一直用默认的自然没问题,一旦人家配置了error.path,就出问题了。

潜规则

这个潜规则的问题,是绝大部分同学写代码中最常见的问题。你想一下,你的自适应全局异常是解决了,但是,带来的影响却是,每一个拦截器都要加上excludePathPatterns这么一个配置。对于使用者来说,这个必须加上某个配置,就是一种潜规则,而且,对于新来的同事而言,他根本不知道这种潜规则,一旦潜规则的代码多了,后续很难维护。

拓展思考

那么不潜规则的代码应该是怎么样的?

当然很多时候,我们必须要潜规则!比如,大数据的同学要求,送过来的日志一定要有应用名。那么,对于业务方而言,他就必须要配置应用名。那么,如何让业务方的同事知道这个潜规则。当然很多同学说,那就直接告诉同事要加某些参数啊。你连肥朝每天的推文都不记得看,你能保证每个同事都记得?

所以总结下来,我们遇到这么一类问题如下:

1.我们需要对拦截器进行一些潜规则参数,比如本文这种,如何优雅潜规则?

2.比如拦截器有顺序要求,比如我们基础框架定义了一个traceInterceptor的拦截器,这个拦截器就必须放在最前。那么问题来了,你怎么保证这个是最前的。有同学就说了,那我用@Order控制啊。那我也写一个和你一样的拦截器,叫feichaoInterceptor,代码和你一模一样,既然一模一样,你怎么保证你的就比我的前了?

3.对于必须要潜规则的场景,如何在反抗的情况下,也能潜到?

对于这几个问题,我们下期再解答。

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

SpringBoot自适应异常处理相关推荐

  1. springboot统一异常处理类及注解参数为数组的写法

    springboot统一异常处理类及注解参数为数组的写法 参考文章: (1)springboot统一异常处理类及注解参数为数组的写法 (2)https://www.cnblogs.com/zhucww ...

  2. springboot自定义异常处理

    springboot自定义异常处理 参考文章: (1)springboot自定义异常处理 (2)https://www.cnblogs.com/SimpleWu/p/10044468.html 备忘一 ...

  3. springboot全局异常处理_SpringBoot:如何优雅地处理全局异常

    之前用springboot的时候,只知道捕获异常使用try{}catch,一个接口一个try{}catch,这也是大多数开发人员异常处理的常用方式,虽然屡试不爽,但会造成一个问题,就是一个Contro ...

  4. springboot统一异常处理及返回数据的处理

    springboot统一异常处理及返回数据的处理 参考文章: (1)springboot统一异常处理及返回数据的处理 (2)https://www.cnblogs.com/renshengruozhi ...

  5. springboot 全局异常处理类

    目录标题 springboot 全局异常处理类 依赖 代码 springboot 全局异常处理类 依赖 <!-- Spring Boot Web 依赖 核心 --><dependen ...

  6. [SpringBoot]全局异常处理

    2019独角兽企业重金招聘Python工程师标准>>> 1. 全局异常处理 2. SpringBoot错误处理原理 3. SpringBoot定制错误页面 4. SpringBoot ...

  7. springboot 默认异常处理

    SpringBoot默认有自定义异常处理的体系,在做SpringBoot项目的时候,如果是抛出了运行时异常,springBoot并会对异常进行处理,返回如下异常信息: {"timestamp ...

  8. SpringBoot 全局异常处理

    下面首先来观察一个程序代码,例如:现在建立一个控制器,而后这个控制器自己抛出一个异常.@Controller public class MemberController extends Abstrac ...

  9. SpringBoot全局异常处理

    1.为什么要全局异常处理 在实际开发中,如果不处理统一处理异常,那么前端在调用后端提供的接口,就会处理各种的异常结构,对于前端来说那可谓是一场灾难,这对前后端的协作也不友好.比如后端路径:/api/v ...

最新文章

  1. 讨厌php机试_[转载]PHP上机面试题
  2. 自己对有上下界的网络流的理解
  3. python 获取向上两级路径_全国计算机二级Python真题解析-1
  4. 08-KNN手写数字识别
  5. IBM Storwize V7000详解:IIS西安大会现场观察
  6. conflicting declaration ‘typedef struct LZ4_stream_t LZ4_stream_t’解决
  7. 【SICP练习】142 练习3.77
  8. Factory Method (工厂方法)
  9. python 查看文件夹大小
  10. grid lightbox gallery
  11. [中文语音识别后文本加标点] 维基百科数据下载和解析(xml-txt)
  12. 大数据处理的基本流程是什么?
  13. android对文件进行加密
  14. App内嵌H5活动页面携带用户token
  15. 哪吒之魔童降世视听语言影评_《哪吒之魔童降世》观后感——不用吹爆,但值得点赞...
  16. 利用AI强化学习训练50级比卡超单挑70级超梦!
  17. 关于R语言多水平线性回归分析
  18. 【C++】const与指针
  19. Matlab应变片仿真,一种基于Matlab/Adams联合仿真的真实路谱再现系统和方法与流程...
  20. pycharm中创建包时加入的_init_.py文件及_all_的作用

热门文章

  1. 教你如何用 Lua 操作文件中的数据
  2. 计算机设计大赛颁奖典礼主持稿的串词,比赛颁奖典礼主持词
  3. 77底盒和86底盒的区别_86型开关底盒的具体参数
  4. C语言必须会写的10道经典题(小白必看!)
  5. 关于学习Python的一点学习总结(10->设置字符串格式)
  6. 华华和月月种树(牛客)
  7. BZOJ 1590.Secret Message 秘密信息(Trie树) [Usaco2008 Dec]【BZOJ计划】
  8. luogu P4847 银河英雄传说V2(FHQ - 平衡树)
  9. 解题报告:P3834 【模板】可持久化线段树 2(主席树)详解
  10. html语言区别大小写吗,用HTML语言制作静态网页基础问题1.标注是否区分大小写?2.下 爱问知识人...