咱們先來看一下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.對於必需要潛規則的場景,如何在反抗的狀況下,也能潛到?
對於這幾個問題,咱們下期再解答。