咱们先来看一下Springboot
的默认效果html
可是绝大部分公司的代码,都是没作自适应处理的,很大一部分缘由在于,你在网上搜索Springboot全局异常处理
,都是搜索到这么一段代码!面试
@ControllerAdvice
public class MyControllerAdvice {
@ResponseBody
@ExceptionHandler(value = Exception.class)
public ResponseEntity<?> errorHandler(Exception ex) {
// 处理异常
}
}
强烈建议先用本身经常使用的搜索引擎搜索一遍,而后再看一下本身公司代码,看看是否是相似这么一段代码再往下看。编程
固然不少同窗可能会说,咱们就已经和客户端约定很好了,只会有json
,不会有返回html
的场景。因此,不作这个适应,其实也是没问题的。可是若是你是作基础架构的同窗,这个功能你是必需要作的,由于你对接的是整个公司的业务部门,Springboot能作,你作相似的基础组件,若是功能比Springboot还差,你让业务方的同窗怎么想?json
固然,对于绝大部分同窗来讲,不作问题也不大。浏览器
可是这样你会错过一个很好的学习机会。什么学习机会?由于不少同窗平时总说,面试造火箭,工做中遇到不懂的问题百度或者谷歌一下就行了。然而,这个问题,你就没这么好搜索到。也就是说,绝大部分人都是面向搜索引擎编程,当遇到搜索引擎没法解决的问题的时候,就是体现你价值的时候,好好珍惜。微信
作不作这个功能我以为不重要,这个宝贵的锻炼解决问题能力的机会是真的很可贵,毕竟,确实大部分功能是真的简单搜索,或者肥朝交流群问问就能解决。架构
不少同窗说,既然搜索不到,那果断一波源码走起。可是,Springboot
源码这么多,请问哪里入手?这个才是重点!这个时候,咱们能够官方文档走一波。app
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.ide
一些同窗说,英文看不懂?我挑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
@ResponseBody
public 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 {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
throw new RuntimeException("这里伪装抛出一个肥朝异常");
//return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
});
}
}
那么就会出现,StackOverflowError
。
由于拦截器出现异常,会掉进你的全局异常处理,而后你的全局异常处理,又进行forward
,又进入了拦截器,而后一直循环。
那么怎么解决这个问题呢?咱们见招拆招,这个时候,我要演示常见的几种不优雅
,可是平时你们都容易作的写法。
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
throw new RuntimeException("这里伪装抛出一个肥朝异常");
//return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public 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.对于必需要潜规则的场景,如何在反抗的状况下,也能潜到?
对于这几个问题,咱们下期再解答。