最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中如何進(jìn)行全局異常處理方式

 更新時間:2024年11月05日 11:34:34   作者:houxian1103  
在SpringBoot開發(fā)過程中,全局異常處理能提高程序的魯棒性并降低代碼耦合,通過使用@RestControllerAdvice和@ExceptionHandler注解,可以實(shí)現(xiàn)對程序異常的全局?jǐn)r截和處理,首先需要自定義一個繼承自ResponseEntityExceptionHandler的異常處理類

前言

在SpringBoot的開發(fā)中,為了提高程序運(yùn)行的魯棒性,我們經(jīng)常需要對各種程序異常進(jìn)行處理,但是如果在每個出異常的地方進(jìn)行單獨(dú)處理的話,這會引入大量業(yè)務(wù)不相關(guān)的異常處理代碼,增加了程序的耦合,同時未來想改變異常的處理邏輯,也變得比較困難。這篇文章帶大家了解一下如何優(yōu)雅的進(jìn)行全局異常處理。

為了實(shí)現(xiàn)全局?jǐn)r截,這里使用到了Spring中提供的兩個注解,@RestControllerAdvice和@ExceptionHandler,結(jié)合使用可以攔截程序中產(chǎn)生的異常,并且根據(jù)不同的異常類型分別處理。下面我會先介紹如何利用這兩個注解,優(yōu)雅的完成全局異常的處理,接著解釋這背后的原理。

如何實(shí)現(xiàn)全局?jǐn)r截?

1.1 自定義異常處理類

在下面的例子中,我們繼承了ResponseEntityExceptionHandler并使用@RestControllerAdvice注解了這個類,接著結(jié)合@ExceptionHandler針對不同的異常類型,來定義不同的異常處理方法。

這里可以看到我處理的異常是自定義異常,后續(xù)我會展開介紹。

  • ResponseEntityExceptionHandler中包裝了各種SpringMVC在處理請求時可能拋出的異常的處理,處理結(jié)果都是封裝成一個ResponseEntity對象。
  • ResponseEntityExceptionHandler是一個抽象類,通常我們需要定義一個用來處理異常的使用@RestControllerAdvice注解標(biāo)注的異常處理類來繼承自ResponseEntityExceptionHandler。
  • ResponseEntityExceptionHandler中為每個異常的處理都單獨(dú)定義了一個方法,如果默認(rèn)的處理不能滿足你的需求,則可以重寫對某個異常的處理。
@Log4j2  
@RestControllerAdvice  
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {  

    /**  
     * 定義要捕獲的異常 可以多個 @ExceptionHandler({})     *  
     * @param request  request  
     * @param e        exception  
     * @param response response  
     * @return 響應(yīng)結(jié)果  
     */  
    @ExceptionHandler(AuroraRuntimeException.class)  
    public GenericResponse customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {  
        AuroraRuntimeException exception = (AuroraRuntimeException) e;  

       if (exception.getCode() == ResponseCode.USER_INPUT_ERROR) {  
           response.setStatus(HttpStatus.BAD_REQUEST.value());  
       } else if (exception.getCode() == ResponseCode.FORBIDDEN) {  
           response.setStatus(HttpStatus.FORBIDDEN.value());  
       } else {  
           response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());  
       }  

        return new GenericResponse(exception.getCode(), null, exception.getMessage());  
    }  

    @ExceptionHandler(NotLoginException.class)  
    public GenericResponse tokenExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {  
        log.error("token exception", e);  
        response.setStatus(HttpStatus.FORBIDDEN.value());  
        return new GenericResponse(ResponseCode.AUTHENTICATION_NEEDED);  
    }  

}

1.2 定義異常碼

這里定義了常見的幾種異常碼,主要用在拋出自定義異常時,對不同的情形進(jìn)行區(qū)分。

@Getter  
public enum ResponseCode {  

    SUCCESS(0, "Success"),  

    INTERNAL_ERROR(1, "服務(wù)器內(nèi)部錯誤"),  

    USER_INPUT_ERROR(2, "用戶輸入錯誤"),  

    AUTHENTICATION_NEEDED(3, "Token過期或無效"),  

    FORBIDDEN(4, "禁止訪問"),  

    TOO_FREQUENT_VISIT(5, "訪問太頻繁,請休息一會兒");  

    private final int code;  

    private final String message;  

    private final Response.Status status;  

    ResponseCode(int code, String message, Response.Status status) {  
        this.code = code;  
        this.message = message;  
        this.status = status;  
    }  

    ResponseCode(int code, String message) {  
        this(code, message, Response.Status.INTERNAL_SERVER_ERROR);  
    }  

}

1.3 自定義異常類

這里我定義了一個AuroraRuntimeException的異常,就是在上面的異常處理函數(shù)中,用到的異常。

每個異常實(shí)例會有一個對應(yīng)的異常碼,也就是前面剛定義好的。

@Getter  
public class AuroraRuntimeException extends RuntimeException {  

    private final ResponseCode code;  

    public AuroraRuntimeException() {  
        super(String.format("%s", ResponseCode.INTERNAL_ERROR.getMessage()));  
        this.code = ResponseCode.INTERNAL_ERROR;  
    }  

    public AuroraRuntimeException(Throwable e) {  
        super(e);  
        this.code = ResponseCode.INTERNAL_ERROR;  
    }  

    public AuroraRuntimeException(String msg) {  
        this(ResponseCode.INTERNAL_ERROR, msg);  
    }  

    public AuroraRuntimeException(ResponseCode code) {  
        super(String.format("%s", code.getMessage()));  
        this.code = code;  
    }  

    public AuroraRuntimeException(ResponseCode code, String msg) {  
        super(msg);  
        this.code = code;  
    }  

}

1.4 自定義返回類型

為了保證各個接口的返回統(tǒng)一,這里專門定義了一個返回類型。

@Getter  
@Setter  
public class GenericResponse<T> {  

    private int code;  

    private T data;  

    private String message;  

    public GenericResponse() {};  

    public GenericResponse(int code, T data) {  
        this.code = code;  
        this.data = data;  
    }  

    public GenericResponse(int code, T data, String message) {  
        this(code, data);  
        this.message = message;  
    }  

    public GenericResponse(ResponseCode responseCode) {  
        this.code = responseCode.getCode();  
        this.data = null;  
        this.message = responseCode.getMessage();  
    }  

    public GenericResponse(ResponseCode responseCode, T data) {  
        this(responseCode);  
        this.data = data;  
    }  

    public GenericResponse(ResponseCode responseCode, T data, String message) {  
        this(responseCode, data);  
        this.message = message;  
    }  
}

實(shí)際測試異常

下面的例子中,我們想獲取到用戶的信息,如果用戶的信息不存在,可以直接拋出一個異常,這個異常會被我們上面定義的全局異常處理方法所捕獲,然后根據(jù)不同的異常編碼,完成不同的處理和返回。

public User getUserInfo(Long userId) {  
    // some logic

    User user = daoFactory.getExtendedUserMapper().selectByPrimaryKey(userId);  
    if (user == null) {  
        throw new AuroraRuntimeException(ResponseCode.USER_INPUT_ERROR, "用戶id不存在");  
    }

    // some logic
    ....
}

以上就完成了整個全局異常的處理過程,接下來重點(diǎn)說說為什么@RestControllerAdvice和@ExceptionHandler結(jié)合使用可以攔截程序中產(chǎn)生的異常?

全局?jǐn)r截的背后原理?

下面會提到@ControllerAdvice注解,簡單地說,@RestControllerAdvice與@ControllerAdvice的區(qū)別就和@RestController與@Controller的區(qū)別類似,@RestControllerAdvice注解包含了@ControllerAdvice注解和@ResponseBody注解。

public class DispatcherServlet extends FrameworkServlet {
    // ......
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);

        // 重點(diǎn)關(guān)注
        initHandlerExceptionResolvers(context);

        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    // ......
}

在initHandlerExceptionResolvers(context)方法中,會取得所有實(shí)現(xiàn)了HandlerExceptionResolver接口的bean并保存起來,其中就有一個類型為ExceptionHandlerExceptionResolver的bean,這個bean在應(yīng)用啟動過程中會獲取所有被@ControllerAdvice注解標(biāo)注的bean對象做進(jìn)一步處理,關(guān)鍵代碼在這里:

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
        implements ApplicationContextAware, InitializingBean {
    // ......
    private void initExceptionHandlerAdviceCache() {
        // ......
        List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
        AnnotationAwareOrderComparator.sort(adviceBeans);

        for (ControllerAdviceBean adviceBean : adviceBeans) {
            ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
            if (resolver.hasExceptionMappings()) {
                // 找到所有ExceptionHandler標(biāo)注的方法并保存成一個ExceptionHandlerMethodResolver類型的對象緩存起來
                this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
                if (logger.isInfoEnabled()) {
                    logger.info("Detected @ExceptionHandler methods in " + adviceBean);
                }
            }
            // ......
        }
    }
    // ......
}

當(dāng)Controller拋出異常時,DispatcherServlet通過ExceptionHandlerExceptionResolver來解析異常,而ExceptionHandlerExceptionResolver又通過ExceptionHandlerMethodResolver 來解析異常, ExceptionHandlerMethodResolver 最終解析異常找到適用的@ExceptionHandler標(biāo)注的方法是這里:

public class ExceptionHandlerMethodResolver {
    // ......
    private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
        List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
        // 找到所有適用于Controller拋出異常的處理方法,例如Controller拋出的異常
        // 是AuroraRuntimeException(繼承自RuntimeException),那么@ExceptionHandler(AuroraRuntimeException.class)和
        // @ExceptionHandler(Exception.class)標(biāo)注的方法都適用此異常
        for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
            if (mappedException.isAssignableFrom(exceptionType)) {
                matches.add(mappedException);
            }
        }
        if (!matches.isEmpty()) {
        /* 這里通過排序找到最適用的方法,排序的規(guī)則依據(jù)拋出異常相對于聲明異常的深度,例如
    Controller拋出的異常是是AuroraRuntimeException(繼承自RuntimeException),那么AuroraRuntimeException
    相對于@ExceptionHandler(AuroraRuntimeException.class)聲明的AuroraRuntimeException.class其深度是0,
    相對于@ExceptionHandler(Exception.class)聲明的Exception.class其深度是2,所以
    @ExceptionHandler(BizException.class)標(biāo)注的方法會排在前面 */
            Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
            return this.mappedMethods.get(matches.get(0));
        }
        else {
            return null;
        }
    }
    // ......
}

整個@RestControllerAdvice處理的流程就是這樣,結(jié)合@ExceptionHandler就完成了對不同異常的靈活處理。

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 在Java中自由塊的執(zhí)行順序

    在Java中自由塊的執(zhí)行順序

    java中的自由塊分為靜態(tài)的自由塊和非靜態(tài)的自由塊。非靜態(tài)自由塊的執(zhí)行時間是:在執(zhí)行構(gòu)造函數(shù)之前。靜態(tài)自由塊的執(zhí)行時間是:class文件加載時執(zhí)行。
    2013-04-04
  • java中File類的三種創(chuàng)建文件夾方法總結(jié)

    java中File類的三種創(chuàng)建文件夾方法總結(jié)

    這篇文章主要給大家介紹了關(guān)于java中File類的三種創(chuàng)建文件夾方法,File類代表文件或目錄路徑名的抽象表達(dá)形式,通過File類提供的方法,我們可以很方便地創(chuàng)建文件夾,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • Java 線程池詳解及實(shí)例代碼

    Java 線程池詳解及實(shí)例代碼

    這篇文章主要介紹了Java 線程池的相關(guān)資料,并符實(shí)例代碼,幫助大家學(xué)習(xí)參考,需要的朋友可以參考下
    2016-09-09
  • SpringBoot3.4集成Seata的詳細(xì)指南

    SpringBoot3.4集成Seata的詳細(xì)指南

    當(dāng)Spring Boot 3.4與Seata 2.0相遇,分布式事務(wù)的復(fù)雜性被壓縮至一行注解,所以這篇文章小編就來和大家詳細(xì)介紹一下SpringBoot3.4集成Seata的具體步驟吧
    2025-04-04
  • Spring?ComponentScan的掃描過程解析

    Spring?ComponentScan的掃描過程解析

    這篇文章主要介紹了spring?ComponentScan的掃描過程解析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • spring BeanProcessor接口詳解

    spring BeanProcessor接口詳解

    這篇文章主要介紹了spring BeanProcessor接口的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用spring,感興趣的朋友可以了解下
    2021-03-03
  • Myeclipse清理項(xiàng)目緩存的幾大方法

    Myeclipse清理項(xiàng)目緩存的幾大方法

    今天小編就為大家分享一篇關(guān)于Myeclipse清理項(xiàng)目緩存的幾大方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 使用Java Collections實(shí)現(xiàn)集合排序的全面指南

    使用Java Collections實(shí)現(xiàn)集合排序的全面指南

    在Java編程中,集合(Collection)是處理數(shù)據(jù)的重要工具之一,Java集合框架提供了豐富的接口和類來操作數(shù)據(jù)集合,而排序是其中最常見的操作之一,本文將詳細(xì)介紹如何使用Collections類對集合進(jìn)行排序,并深入探討其背后的原理和使用場景,需要的朋友可以參考下
    2025-02-02
  • Struts2學(xué)習(xí)教程之輸入校驗(yàn)示例詳解

    Struts2學(xué)習(xí)教程之輸入校驗(yàn)示例詳解

    這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)教程之輸入校驗(yàn)的相關(guān)資料,文中通過示例介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用struts2具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • Java實(shí)現(xiàn)對象復(fù)制的方法實(shí)例

    Java實(shí)現(xiàn)對象復(fù)制的方法實(shí)例

    這篇文章主要介紹了Java實(shí)現(xiàn)對象復(fù)制的方法實(shí)例,深復(fù)制:復(fù)制出來的對象中的變量(包括基本類型和字符串)和原來的對象的值都相同,引用對象也會指向復(fù)制出來的對象,需要的朋友可以參考下
    2023-08-08

最新評論

清流县| 通城县| 武穴市| 正蓝旗| 云浮市| 曲松县| 千阳县| 沧源| 阿尔山市| 华蓥市| 简阳市| 壤塘县| 和平县| 醴陵市| 淅川县| 木兰县| 法库县| 手机| 镇原县| 边坝县| 连州市| 金门县| 读书| 刚察县| 犍为县| 乡城县| 饶河县| 温泉县| 遵义县| 吉林省| 大荔县| 宿迁市| 布拖县| 鄂托克前旗| 贵阳市| 锦州市| 泸溪县| 苏尼特左旗| 青龙| 福鼎市| 大悟县|