1. 程式人生 > >Spring異常解析原始碼分析

Spring異常解析原始碼分析

微信公眾號:
關注回覆java 1024G最新java學習資料(包含大資料資料)免費領;

異常處理重要性

  1. 良好的異常處理體系,便於對程式的後期維護
  2. 當發生錯誤時,程式不至於崩潰,提高程式健壯性
  3. 當發生錯誤時,可以在短時間內找定位問題所在
  4. 當發生錯誤時,避免異常棧裸奔,暴露底層架構,提高專案安全性

Spring統一異常方式

  • 使用 @ ExceptionHandler 註解(缺點:異常處理的方法必須與出錯的方法在同一個Controller裡面,不能全域性處理)
1    // 需要捕捉的異常
2    @ExceptionHandler({ BizException.class })
3        // Value用於設定response的狀態碼,例如404,200等,reason用於響應,可以是內容語句。
4    @ResponseStatus(code=HttpStatus.BAD_REQUEST,reason="bad request")
5      // 可以返回Json也可以進行跳轉
6      @ResponseBody
7    public ServerResponse<?> exception(BizException e) {
8        return ServerResponse.createByErrorMessage(e.getMessage());
9    }
  • 實現 HandlerExceptionResolver 介面
11 @Component  
2 public class GlobalExceptionResolver implements HandlerExceptionResolver{  
3
4     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,  Exception ex) {  
5         //Object handler ----> HandlerMethod 也可能為null
6         // 可以通過handler進行特定處理
7        ..............
8     }  
9}   

  • 使用@ControllerAdvice+ @ ExceptionHandler註解
 [email protected]
 [email protected]
 3public class GlobalExceptionResolver {
 4    @ResponseStatus(HttpStatus.BAD_REQUEST)
 5    // 可以不指定特定的異常即預設攔截所有異常
 6    @ExceptionHandler(HttpMessageNotReadableException.class)
 7    public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
 8        return  ServerResponse.createByErrorMessage(e.getMessage())
 9    }
10 // 其他程式碼省略
11}

SpringMVC異常處理原始碼剖析

  • 從DispatcherServlet的doDispatch方法入手
 1protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 2        try {
 3            //省略請求處理程式碼部分
 4            }catch (Exception ex) {
 5                dispatchException = ex;
 6            }
 7           // 捕捉異常後呼叫processDispatchResult方法
 8            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 9        }
10        finally {
11
12        }
13    }
  • processDispatchResult如何處理呢
 1private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
 2            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
 3      // 先判斷異常是否為null    
 4      if (exception != null) {
 5            if (exception instanceof ModelAndViewDefiningException) {
 6                logger.debug("ModelAndViewDefiningException encountered", exception);
 7                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
 8            }else {
 9                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
10                mv = processHandlerException(request, response, handler, exception);
11                errorView = (mv != null);
12            }
13        }
14    }
  • processHandlerException核心程式碼
 1protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
 2            Object handler, Exception ex) throws Exception {
 3                // 省略了不關係部分(異常解析器排序)
 4        ModelAndView exMv = null;
 5        for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
 6                         // 呼叫resolveException方法
 7                         // Spring 自帶的異常處理器暫時不講本質上和我們自定義差不多
 8            exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
 9            if (exMv != null) {
10                               // 只要獲取ModeAndView終止處理     
11                break;
12            }
13        }
14                 // 如果沒獲得直接丟擲異常
15        throw ex;
16    }
  • Spring自帶異常解析器主要介面和實現類
    • AbstractHandlerMethodExceptionResolver和ExceptionHandlerExceptionResolver負責解析@ExceptionHandle
    • ResponseStatusExceptionResolver解析@ResponseStatus
    • DefaultHandlerExceptionResolver按照不同的型別分別對異常進行解析
    • SimpleMappingExceptionResolver: 通過配置的異常類和view的對應關係來解析異常

攔截404

  上面介紹的方法並不能攔截404,為什麼要攔截404呢?首先為了產品的安全,不隨便暴露後臺所用的中介軟體,避免黑客利用中介軟體本身的漏洞攻擊網站,另外也可以專案與使用者有良好的互動。

  • 利用Spring MVC的最精確匹配原則(@requestMapping("*)攔截的這個方法返回一個自定義的404介面)

  • 利用web容器提供的error-page

1<error-page>
2    // 也可以攔截其他錯誤碼比如500
3    <error-code>404</error-code>
4    // 確保resource目錄不被spring攔截
5    <location>/resource/view/404.htm</location>
6  </error-page>
  • 重寫DispatcherServlet的noHandlerFound方法
 1protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception{
 2        if(pageNotFoundLogger.isWarnEnabled())
 3            pageNotFoundLogger.warn((new StringBuilder()).append("No mapping found for HTTP request with URI [").append(getRequestUri(request)).append("] in DispatcherServlet with name '").append(getServletName()).append("'").toString());
 4        if(throwExceptionIfNoHandlerFound){
 5            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders());
 6        } else{
 7            //response.sendError(404);
 8            // return;
 9            // 修改為
10            // 定義一個請求路徑為404的Controller方法
11            response.sendRedirect(request.getContextPath() + "/404"); 
12        }
13    }

專案中異常處理方案

 [email protected]
 2public class GlobalExceptionResolver implements HandlerExceptionResolver {
 3
 4    public static final String AJAX="X-Requested-With";
 5
 6    public ModelAndView resolveException(HttpServletRequest request,
 7            HttpServletResponse response, Object handler, Exception ex) {
 8     // 區分是否是ajax請求  
 9         boolean isAjax = isAjax(request);
10         return handleException(request,response,handler,ex,isAjax);
11    }
12    /**
13     * 判斷當前請求是否為非同步請求.
14     * @param request
15     * @param response
16     * @return boolean
17     * @author zhaoxin
18     * @date 2018年9月18日 上午10:59:35
19     */
20    private boolean isAjax(HttpServletRequest request){
21        return StrUtil.isNotBlank(request.getHeader(AJAX));
22    }
23    /**
24     * 處理異常
25     * @return ModelAndView
26     * @author zhaoxin
27     * @date 2018年9月18日 上午11:52:40
28     */
29    private ModelAndView handleException(HttpServletRequest request,
30            HttpServletResponse response, Object handler,
31            Throwable ex, boolean isajax) {
32        //異常資訊記錄到日誌檔案中
33        LogUtil.logError("Capture global exceptions:"+ex.getMessage());
34        LogUtil.logException(ex);
35        //分普通請求和ajax請求分別處理
36        if(isajax){
37            return handleAjax(request,response,handler,ex);
38        }else{
39            return handlerNotAjax(request,response,handler,ex);
40        }
41    }
42
43    /**
44     * ajax異常處理並返回.
45     * @param request
46     * @param response
47     * @param handler
48     * @param initialEx
49     */
50    private ModelAndView handleAjax(HttpServletRequest request,
51            HttpServletResponse response, Object handler,Throwable initialEx){
52        response.setHeader("Cache-Control", "no-store");
53                // 返回JsonView
54        ModelAndView mav = new ModelAndView(new FastJsonJsonView());
55        mav.addObject("msg", "System exceptions please check logs");
56        mav.addObject("status", Const.RespondeCode.ERROR.getCode());
57        mav.addObject("success",false);
58        return mav;
59    }
60    /**
61     * 普通頁面異常處理並返回.
62     * @param request
63     * @param response
64     * @param handler
65     * @param deepestException
66     * @return
67     */
68    private ModelAndView handlerNotAjax(HttpServletRequest request,HttpServletResponse response, Object handler, Throwable ex) {
69        Map<String, Object> model = new HashMap<>();
70        model.put("message", "System exceptions please check logs");
71        model.put("ex", ex);
72        return new ModelAndView("common/error500", model);
73    }
74
75
76}

求關注

相關推薦

Spring異常解析原始碼分析

微信公眾號: 關注回覆java 1024G最新java學習資料(包含大資料資料)免費領; 異常處理重要性 良好的異常處理體系,便於對程式的後期維護 當發生錯誤時,程式不至於崩潰,提高程式健壯性 當發生錯誤時,可以在短時間內找定位問題所在 當發生錯誤時,避免異常棧

深入研究Spring-IoC:原始碼分析依賴注入

1.前言 對於容器建立的過程已經闡述過一遍了,下面是依賴注入的問題。Spring提供的依賴注入的方法主要由兩種:一種是通過getBean的方法;另一種是通過註解@Autowaire。 需要指出的是依賴注入的過程是使用者第一次向ioc容器索要Bean的時候開始生產的,也可以通過設定

深入研究Spring-IoC:原始碼分析容器建立

1.前言 從之前的分析中可以知道IOC容器的建立大致分為3步:Resource定位、BeanDefinition載入解析、向容器註冊BeanDefinition。 Tiny-spring手動實現了Spring框架,通過對這個原始碼的解讀可以更好更有效的理解Spring。 2.

Spring Developer Tools 原始碼分析:三、重啟自動配置'

接上文 Spring Developer Tools 原始碼分析:二、類路徑監控,接下來看看前面提到的這些類是如何配置,如何啟動的。 spring-boot-devtools 使用了 Spring Boot 的自動配置方式,我們先關注本地開發環境中自動重啟的部分。 在 LocalDevToolsAut

Spring Cloud Zuul原始碼分析

如何使用Spring Cloud Zuul? 之前的文章中,我們學習了Spring Cloud Zuul如何使用,這裡再回顧下: 1.引入依賴,在啟動類中新增@EnableZuulProxy,宣告這是一個Zuul代理。 2.註冊到Eureka Server,啟動服務,訪問這個埠,ur

spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(三)

SpringBootMonitor spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(一) spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(二)

spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(二)

SpringBootMonitor spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(一) spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(二)

spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(一)

SpringBootMonitor spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(一) spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(二) spring-boot-ad

深入分析集合併發修改異常原始碼分析)java.util.ConcurrentModificationException

          在我們迭代操作集合時,有時可能需要改變集合的元素,若操作不當,經常都會出現不正確的結果或者併發操作異常,如下: Exception in thread "main" java.util.ConcurrentModificatio

spring事務管理原始碼分析(一)配置和事務增強代理的生成流程

在本篇文章中,將會介紹如何在spring中進行事務管理,之後對其內部原理進行分析。主要涉及 @EnableTransactionManagement註解為我們做了什麼? 為什麼標註了@Transactional註解的方法就可以具有事務的特性,保持了資料的ACID特性?spring到底是如何具有這樣

Android7.1 [Camera] cam_board.xml 檔案解析原始碼分析(一)

        原始碼平臺:rk3399           RK支援了很多個攝像頭,在驅動目錄hardware/rockchip/camer

spring mvc核心原始碼分析

前言 自研究了spring security核心原始碼以來,在實踐使用的基礎上更為深入的學習了它的思想.我的java世界彷彿被打開了一扇大門,開始對研究原始碼有種渴望.打算先看一輪核心原始碼,滿足目前工作需要,待對boot有個整體的瞭解之後再逐個細細研究 spring m

Spring component-scan原始碼分析(一)

在XML中配置component-scan通常如下 <context:component-scan base-package="xxx"> <context:exclude-filter type="annotation" expressio

Spring component-scan原始碼分析(二) -- @Configuration註解處理

上篇文章Spring component-scan原始碼分析(一) – XML解析分析了Spring解析<context:component-scan …/>標籤時,把掃描到的合適的類封裝成BeanDefinition加入Sping容器中,本篇分析S

Spring component-scan原始碼分析(三) -- @Autowired等註解的處理

本篇文章分析注入註解(@Autowired、@Value等)的處理,其邏輯在AutowiredAnnotationBeanPostProcessor類中。 可以看到AutowiredAnnotationBeanPostProcessor類實現了一些增強處理的

Spring-Cloud-Gateway原始碼分析系列| Spring-Cloud-Gateway初始化

推薦 Spring Boot/Cloud 視訊: Spring-Cloud專案使用EnableAutoConfiguration註解自動 初始化配置資訊,Spring-Cloud-Gateway同樣,Spring-Cloud-Gateway下的spring.f

Spring-Cloud-Gateway原始碼分析系列 | Spring-Cloud-Gateway之GatewayProperties初始化載入

推薦 Spring Boot/Cloud 視訊: 在Spring-Cloud-Gateway初始化時我們在GatewayAutoConfiguration配置中看到了有初始化載入GatewayProperties例項的配置,接下來學習下GatewayPrope

spring事物--04原始碼分析-事務處理攔截器的實現分析

事務處理攔截器的實現分析 通過上面的分析,很明確spring在事務方面aop是怎麼玩的了。那麼真正要處理事務是ProxyFactory.getObject() 方法返回的代理物件,通過呼叫代理物件的方法時,攔截器有一個invoker() 方法會被回撥(aop的玩法)。

Spring Core Container 原始碼分析三:Spring Beans 初始化流程分析

前言 本文是筆者所著的 Spring Core Container 原始碼分析系列之一; 本篇文章主要試圖梳理出 Spring Beans 的初始化主流程和相關核心程式碼邏輯; 本文轉載自本人的部落格,傷神的部落格 http://www.shangyang.me/2017/

Spring Core Container 原始碼分析七:註冊 Bean Definitions

前言 原本以為,Spring 通過解析 bean 的配置,生成並註冊 bean defintions 的過程不太複雜,比較簡單,不用單獨開闢一篇博文來講述;但是當在分析前面兩個章節有關 @Autowired、@Component、@Service 註解的注入機制的時候,發現,如果沒有對有關 bea