日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring 异常处理三种方式

發(fā)布時間:2025/3/12 javascript 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring 异常处理三种方式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Spring 異常處理三種方式

  • 異常處理方式一. @ExceptionHandler
  • 異常處理方式二. 實現(xiàn)HandlerExceptionResolver接口
  • 異常處理方式三. @ControllerAdvice+@ExceptionHandler
  • 三種方式比較說明(強烈推薦各位看一下,我覺得自己總結(jié)的比較多,嘿嘿,不對之處請指出)
  • 文章目錄

    • Spring 異常處理三種方式
      • 問題描述: 假如對異常不進(jìn)行處理?
    • 異常處理的方式有三種:
      • 一. Controller層面上異常處理 @ExceptionHandler
        • 原理說明:
      • 二. 全局級別異常處理器 實現(xiàn)HandlerExceptionResolver接口
        • 原理分析:
      • 三.全局級別異常處理器 @ControllerAdvice+@ExceptionHandler
      • 四.比較說明.

    問題描述: 假如對異常不進(jìn)行處理?

    假如SpringMvc我們不對異常進(jìn)行任何處理, 界面上顯示的是這樣的.

    異常處理的方式有三種:

    一. Controller層面上異常處理 @ExceptionHandler

    說明:針對可能出問題的Controller,新增注解方法@ExceptionHandler.

    @Controller @RequestMapping("/testController") public class TestController {@RequestMapping("/demo1")@ResponseBodypublic Object demo1(){int i = 1 / 0;return new Date();}@ExceptionHandler({RuntimeException.class})public ModelAndView fix(Exception ex){System.out.println("do This");return new ModelAndView("error",new ModelMap("ex",ex.getMessage()));} }

    注意事項:

  • 一個Controller下多個@ExceptionHandler上的異常類型不能出現(xiàn)一樣的,否則運行時拋異常.
    Ambiguous @ExceptionHandler method mapped for;
  • @ExceptionHandler下方法返回值類型支持多種,常見的ModelAndView,@ResponseBody注解標(biāo)注,ResponseEntity等類型都OK.
  • 原理說明:

    代碼片段位于:org.springframework.web.servlet.DispatcherServlet#doDispatch

    執(zhí)行@RequestMapping方法拋出異常后,Spring框架 try-catch的方法捕獲異常, 正常邏輯發(fā)不發(fā)生異常都會走processDispatchResult流程 ,區(qū)別在于異常的參數(shù)是否為null .

    HandlerExecutionChain mappedHandler = null;Exception dispatchException = null;ModelAndView mv = null;try{mappedHandler=getHandler(request); //根據(jù)請求查找handlerMapping找到controllerHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//找到處理器適配器HandlerAdapterif(!mappedHandler.applyPreHandle(request,response)){ //攔截器preHandlereturn ;} mv=ha.handle(request,response); //調(diào)用處理器適配器執(zhí)行@RequestMapping方法mappedHandler.applyPostHandle(request,response,mv); //攔截器postHandle}catch(Exception ex){dispatchException=ex;}processDispatchResult(request,response,mappedHandler,mv,dispatchException) //將異常信息傳入了

    代碼片段位于:org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    如果@RequestMapping方法拋出異常,攔截器的postHandle方法不執(zhí)行,進(jìn)入 processDispatchResult,判斷入?yún)?dispatchException,不為null , 代表發(fā)生異常,調(diào)用processHandlerException處理,

    代碼片段位于:org.springframework.web.servlet.DispatcherServlet#processHandlerException

    this當(dāng)前對象指dispatchServlet,handlerExceptionResolvers可以看到有三個HandlerExceptionResolver, 這三個是<mvc:annotation-driven />幫我們注冊的. 遍歷有序集合handlerExceptionResolvers,調(diào)用接口的resolveException方法.

    記錄mvc:annotation-driven/注冊的第一個 HandlerExceptionResolver : ExceptionHandlerExceptionResolver, 繼承關(guān)系如下面所示.

    代碼片段位于:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#resolveException

    AbstractHandlerExceptionResolver 和 AbstractHandlerMethodExceptionResolver名字看起來非常相似. 這里AbstractHandlerExceptionResolver 的shouldApplyTo都返回 true, logException用來記錄日志、prepareResponse方法用來設(shè)置response的Cache-Control. 異常處理方法就位于doResolveException.

    代碼片段位于:org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver#shouldApplyTo

    接口方法實現(xiàn)是AbstractHandlerExceptionResolver的resolveException,先判斷 shouldApplyTo, AbstractHandlerExceptionResolver 和子類AbstractHandlerMethodExceptionResolver都實現(xiàn)了shouldApplyTo方法,子類的shouldApplyTo都調(diào)用父類AbstractHandlerExceptionResolver的shouldApplyTo.

    查看父類AbstractHandlerExceptionResolver的shouldApplyTo方法.

    代碼片段位于:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#shouldApplyTo

    Spring初始化的時候并沒有額外配置 , 所以mappedHandlers和mappedHandlerClasses都為null, 可以在這塊擴(kuò)展進(jìn)行篩選 ,AbstractHandlerExceptionResolver提供了 setMappedHandlerClasses 、setMappedHandlers用于擴(kuò)展.

    代碼片段位于:org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver#doResolveException

    代碼片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException

    似曾相識的ServletInvocableHandlerMethod,getExceptionHandlerMethod目的就是獲取 針對異常的處理方法,沒找到的話這里就直接返回了,找到了執(zhí)行異常處理方法;

    之后同Spring請求方法執(zhí)行一樣的處理方式,設(shè)置argumentResolvers、returnValueHandlers,之后進(jìn)行調(diào)用異常處理方法,

    @ExceptionHandler的方法入?yún)⒅С?#xff1a;Exception ;SessionAttribute 、 RequestAttribute注解 ; HttpServletRequest 、HttpServletResponse、HttpSession.

    @ExceptionHandler方法返回值常見的可以是: ModelAndView 、@ResponseBody注解、ResponseEntity;

    getExceptionHandlerMethod說明: 獲取對應(yīng)的@ExceptionHandler方法,封裝成ServletInvocableHandlerMethod返回.

    代碼片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod

    exceptionHandlerCache是針對Controller層面的@ExceptionHandler的處理方式,而exceptionHandlerAdviceCache是針對@ControllerAdvice的處理方式. 這兩個屬性都位于ExceptionHandlerExceptionResolver中.

    handlerType指代Controller的class屬性,嘗試從緩存A exceptionHandlerCache 中根據(jù)controller的class 查找ExceptionHandlerMethodResolver; 緩存A之前沒存儲過Controller的class ,所以新建一個ExceptionHandlerMethodResolver 加入緩存中. ExceptionHandlerMethodResolver 的初始化工作一定做了某些工作!

    resolveMethod方法:根據(jù)異常對象讓 ExceptionHandlerMethodResolver 解析得到 method , 匹配到異常處理方法 就直接封裝成對象 ServletInvocableHandlerMethod ; 就不會再去走@ControllerAdvice里的異常處理器了. 這里說明了,ExceptionHandlerMethodResolver 初始化的時候完成存儲 @ExceptionHandler.

    查看ExceptionHandlerMethodResolver 初始化工作內(nèi)容:

    代碼片段位于:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver

    handlerType為傳入的Controller的class屬性,通過EXCEPTION_HANDLER_METHODS選出 class 中標(biāo)注@ExceptionHandler的方法,解析@Exception注解的value值(class類型的數(shù)組),并加入到當(dāng)前ExceptionHandlerMethodResolver的mappedMethods集合中,key為 異常類型 ,value為 method.

    如果@ExceptionHandler的 value屬性為空,就會將方法入?yún)⒅械腡hrowable的子類作為異常類型. @ExceptionHandler的value屬性和方法入?yún)⒉荒芡瑫r都為空,否則會拋出異常.

    ExceptionHandlerMethodResolver完成了初始化工作,如何根據(jù)當(dāng)前發(fā)生異常類型查找到對應(yīng)方法?

    代碼片段位于:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#resolveMethod

    resolveMethodByExceptionType根據(jù)當(dāng)前拋出異常尋找 匹配的方法,并且做了緩存,以后遇到同樣的異常可以直接走緩存取出method,

    代碼片段位于:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#resolveMethodByExceptionType

    resolveMethodByExceptionType方法,嘗試從緩存A:exceptionLookupCache中根據(jù) 異常class類型獲取Method ,初始時候肯定緩存為空 ,就去 遍歷ExceptionHandlerMethodResolver的mappedMethods(上面提及了key為異常類型,value為method), exceptionType為當(dāng)前@RequestMapping方法拋出的異常,判斷當(dāng)前異常類型是不是@ExceptionHandler中value聲明的子類或本身,滿足條件就代表匹配上了;可能存在多個匹配的方法,使用ExceptionDepthComparator排序,排序規(guī)則是按照繼承順序來(繼承關(guān)系越靠近數(shù)值越小,當(dāng)前類最小為0,頂級父類Throwable為int最大值),排序之后選取繼承關(guān)系最靠近的那個,并且存入ExceptionHandlerMethodResolver的exceptionLookupCache中,key為當(dāng)前拋出的異常,value為解析出來的匹配method.

    至此 @ExceptionHandler Spring讀取到并解析出來完畢了,后續(xù)流程和Spring正常請求流程一樣,包括@ExceptionHandler的方法入?yún)ⅰ⒎椒ǚ祷刂?

    @ExceptionHandler的方法入?yún)⒅С?#xff1a;Exception ;SessionAttribute 、 RequestAttribute注解 ; HttpServletRequest 、HttpServletResponse、HttpSession.

    @ExceptionHandler方法返回值常見的可以是: ModelAndView 、@ResponseBody注解、ResponseEntity;

    二. 全局級別異常處理器 實現(xiàn)HandlerExceptionResolver接口

    public class MyHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {System.out.println("發(fā)生全局異常!");ModelMap mmp=new ModelMap();mmp.addAttribute("ex",ex.getMessage());return new ModelAndView("error",mmp);}}

    使用方式: 只需要將該Bean加入到Spring容器,可以通過Xml配置,也可以通過注解方式加入容器;

    ? 方法返回值不為null才有意義,如果方法返回值為null,可能異常就沒有被捕獲.

    缺點分析:比如這種方式全局異常處理返回JSP、velocity等視圖比較方便,返回json或者xml等格式的響應(yīng)就需要自己實現(xiàn)了.如下是我實現(xiàn)的發(fā)生全局異常返回JSON的簡單例子.

    public class MyHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {System.out.println("發(fā)生全局異常!");ModelMap mmp=new ModelMap();mmp.addAttribute("ex",ex.getMessage());response.addHeader("Content-Type","application/json;charset=UTF-8");try {new ObjectMapper().writeValue(response.getWriter(),ex.getMessage());response.getWriter().flush();} catch (IOException e) {e.printStackTrace();}return new ModelAndView();}}

    原理分析:

    記得之前介紹了 DispatcherServlet的HandlerExceptionResolver集合,這種方式的HandlerExceptionResolver就是從DispatcherServlet的HandlerExceptionResolver集合入手的.

    代碼片段位于:org.springframework.web.servlet.DispatcherServlet#processHandlerException

    this對象指代DispatcherServlet,和上面方式對比,發(fā)現(xiàn)我們只是將MyHandlerExceptionResolver 加入到Spring容器,dispatchServlet 的 handlerExceptionResolvers屬性就多了我們自己定義的全局異常解析器;

    ExceptionHandlerMethodResolver是用來解析@Controller層面的@ExceptionHandler注解,當(dāng)前Controller沒有找到@ExceptionHandler來處理自己拋出的異常,才遍歷下一個HandlerExceptionResolver;

    HandlerExceptionResolver是個有序集合,Spring注冊的HandlerExceptionResolver調(diào)用resolveException都失敗之后,才輪到我們自定義的MyHandlerExceptionResolver ;而且我們自定義的MyHandlerExceptionResolver 就沒法使用SpringMvc的注解等等.

    我們只是將HandlerExceptionResolver加入到Spring容器中,Spring是如何通知給DispatcherServlet呢?

    代碼片段位于:org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers

    initHandlerExceptionResolvers只是DispatcherServlet初始化策略方法initStrategies中的一小步,可以看到只要是SpringMvc父子容器中注冊的HandlerExceptionResolver類型實例,DispatcherServlet都會自動將其加入到DispatcherServlet的handlerExceptionResolvers中. 所以我們需要做的只是實現(xiàn)HandlerExceptionResolver接口,并且納入Spring容器管理即可.

    三.全局級別異常處理器 @ControllerAdvice+@ExceptionHandler

    簡單使用方法:

    @ControllerAdvice public class GlobalController {@ExceptionHandler(RuntimeException.class)public ModelAndView fix1(Exception e){System.out.println("全局的異常處理器");ModelMap mmp=new ModelMap();mmp.addAttribute("ex",e);return new ModelAndView("error",mmp);} } package com.abc.admin;import com.github.pagehelper.Page; import com.viready.scpms.pojo.bean.ResponseResult; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;@ControllerAdvice public class ControllerAdviceHandler implements ResponseBodyAdvice<Object> {@ResponseBody@ExceptionHandler(value = Throwable.class)public ResponseResult catchAllError(HttpServletRequest request, HttpServletResponse response, Throwable error) {return ResponseResult.exception(error);}@Overridepublic boolean supports(MethodParameter method, Class<? extends HttpMessageConverter<?>> clazz) {return method.getContainingClass().getPackage().getName().startsWith("com.abc") &&(method.getContainingClass().getAnnotation(RestController.class) != null ||(method.getMethod() != null && method.getMethod().getAnnotation(ResponseBody.class) != null));}@Overridepublic Object beforeBodyWrite(Object result, MethodParameter methodParameter, MediaType mediaType,Class<? extends HttpMessageConverter<?>> aClass,ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {return result instanceof ResponseResult ? result :result instanceof Page ? ResponseResult.page((Page<?>) result) :ResponseResult.successData(result);} }

    用法說明: 這種情況下 @ExceptionHandler 與第一種方式用法相同,返回值支持ModelAndView,@ResponseBody等多種形式.

    方式一提到ExceptionHandlerExceptionResolver不僅維護(hù)@Controller級別的@ExceptionHandler,同時還維護(hù)的@ControllerAdvice級別的@ExceptionHandler.

    代碼片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod

    isApplicableToBeanType方法是用來做條件判斷的,@ControllerAdvice注解有很多屬性用來設(shè)置條件,basePackageClasses、assignableTypes、annotations等,比如我限定了annotations為注解X, 那標(biāo)注了@X 的ControllerA就可以走這個異常處理器,ControllerB就不能走這個異常處理器.

    現(xiàn)在問題的關(guān)鍵就只剩下了exceptionHandlerAdviceCache是什么時候掃描@ControllerAdvice的,下面的邏輯和@ExceptionHandler的邏輯一樣了.

    exceptionHandlerAdviceCache初始化邏輯:

    代碼片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#afterPropertiesSet

    afterPropertiesSet是Spring bean創(chuàng)建過程中一個重要環(huán)節(jié).

    代碼片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache

    ControllerAdviceBean.findAnnotatedBeans方法查找了SpringMvc父子容器中標(biāo)注 @ControllerAdvice 的bean, new ExceptionHandlerMethodResolver初始化時候解析了當(dāng)前的@ControllerAdvice的bean的@ExceptionHandler,加入到ExceptionHandlerExceptionResolverexceptionHandlerAdviceCache中,key為ControllerAdviceBean,value為ExceptionHandlerMethodResolver . 到這里exceptionHandlerAdviceCache就初始化完畢.

    查找SpringMvc父子容器中所有@ControllerAdivce的bean的方法

    代碼片段位于:org.springframework.web.method.ControllerAdviceBean#findAnnotatedBeans

    遍歷了SpringMVC父子容器中所有的bean,標(biāo)注ControllerAdvice注解的bean加入集合返回.

    四.比較說明.

    @Controller+@ExceptionHandler、HandlerExceptionResolver接口形式、@ControllerAdvice+@ExceptionHandler優(yōu)缺點說明:

    在Spring4.3.0版本下,

    1.優(yōu)先級來說,@Controller+@ExceptionHandler優(yōu)先級最高,其次是@ControllerAdvice+@ExceptionHandler,最后才是HandlerExceptionResolver,說明假設(shè)三種方式并存的情況 優(yōu)先級越高的越先選擇,而且被一個捕獲處理了就不去執(zhí)行其他的.

    ? 2. 三種方式都支持多種返回類型,@Controller+@ExceptionHandler、@ControllerAdvice+@ExceptionHandler可以使用Spring支持的@ResponseBody、ResponseEntity,而HandlerExceptionResolver方法聲明返回值類型只能是 ModelAndView,如果需要返回JSON、xml等需要自己實現(xiàn).

    ? 3.緩存利用,@Controller+@ExceptionHandler的緩存信息在ExceptionHandlerExceptionResolver的exceptionHandlerCache,@ControllerAdvice+@ExceptionHandler的緩存信息在ExceptionHandlerExceptionResolver的exceptionHandlerAdviceCache中, 而HandlerExceptionResolver接口是不做緩存的,在前面兩種方式都fail的情況下才會走自己的HandlerExceptionResolver實現(xiàn)類,多少有點性能損耗.

    EOF

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結(jié)

    以上是生活随笔為你收集整理的Spring 异常处理三种方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。