自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Boot 優(yōu)雅捕捉異常的幾種姿勢!

開發(fā) 前端
通過@ControllerAdvice和@ExceptionHandler注解實現(xiàn)全局異常攔截,在之前的文章中我們有多次介紹過,它可以攔截controller層請求方法拋出的異常信息,同時外加@ ResponseBody注解,可以實現(xiàn)響應(yīng)類型為json格式。

01、背景介紹

在上文中,我們介紹了在 Spring Boot 中實現(xiàn)接口數(shù)據(jù)格式的統(tǒng)一返回處理實現(xiàn),其中就包括程序運行時的異常處理,通過全局異常處理器,可以簡化代碼邏輯,統(tǒng)一響應(yīng)格式。

其實在 Spring Boot 中,針對controller層的異常處理有很多種辦法。今天通過這篇文章,我們就一起來總結(jié)一下相關(guān)異常處理的實現(xiàn)方式。

02、方案實踐

在 Spring Boot 中針對controller層的異常處理,有兩種常用實現(xiàn)方式,都可以達到簡化代碼邏輯的效果。

  • 方式一:通過@ControllerAdvice和@ExceptionHandler注解實現(xiàn)全局異常的處理
  • 方式二:通過實現(xiàn)HandlerExceptionResolver接口來完成全局異常的處理

下面我們一起來看看具體實現(xiàn)。

2.1、全局異常處理方式一

通過@ControllerAdvice和@ExceptionHandler注解實現(xiàn)全局異常攔截,在之前的文章中我們有多次介紹過,它可以攔截controller層請求方法拋出的異常信息,同時外加@ ResponseBody注解,可以實現(xiàn)響應(yīng)類型為json格式。

例如,現(xiàn)在有兩種異常類型NullPointerException和Exception,分別對其進行捕捉,具體實現(xiàn)如下!

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 優(yōu)先處理空指針異常
     * @param e
     * @return
     */
    @ExceptionHandler(value = {NullPointerException.class})
    @ResponseBody
    public Object nullPointerExceptionHandler(HttpServletRequest request, NullPointerException e){
        LOGGER.error("發(fā)生空指針異常,請求地址:{}, 錯誤信息:{}", request.getRequestURI(), e.getMessage());
        return ResultMsg.fail(500, e.getMessage());
    }


    /**
     * 兜底處理其它異常
     * @param e
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public Object exceptionHandler(HttpServletRequest request, Exception e){
        LOGGER.error("未知異常,請求地址:{}, 錯誤信息:{}", request.getRequestURI(), e.getMessage());
        return ResultMsg.fail(999, e.getMessage());
    }
}

測試代碼,如下:

@RestController
public class HelloController {

    @GetMapping(value = "/add")
    public String hello(){
        if(1 ==1){
            throw new NullPointerException("空指針測試");
        }
        return "hello world";
    }

    @GetMapping(value = "/delete")
    public String delete(){
        if(1 ==1){
            throw new RuntimeException("其它測試");
        }
        return "hello world";
    }
}

啟動服務(wù)后,在瀏覽器中請求http://localhost:8080/add,結(jié)果如下:

圖片圖片

請求http://localhost:8080/delete,結(jié)果如下:

圖片圖片

結(jié)果與預(yù)期一致。

2.1.1、自定義異常類實現(xiàn)

很多場景下,我們希望通過自定義異常類來返回相關(guān)錯誤信息,如何實現(xiàn)呢?

首先自定義一個異常類CustomerException。

public class CustomerException extends RuntimeException {

    private Integer code;

    public Integer getCode() {
        return code;
    }

    public CustomerException(String message) {
        super(message);
        this.code = 500;
    }

    public CustomerException(Integer code, String message) {
        super(message);
        this.code = code;
    }
}

然后,在全局異常處理器中增加相關(guān)的捕捉方法。

/**
 * 處理自定義的異常
 * @param e
 * @return
 */
@ExceptionHandler(value = {CustomerException.class})
@ResponseBody
public Object customerExceptionHandler(HttpServletRequest request, CustomerException e){
    LOGGER.error("發(fā)生業(yè)務(wù)異常,請求地址:{}, 錯誤信息:{}", request.getRequestURI(), e.getMessage());
    return ResultMsg.fail(e.getCode(), e.getMessage());
}

測試代碼,如下:

@GetMapping(value = "/update")
public String update(){
    if(1 ==1){
        throw new CustomerException(4003, "請求ID不能為空");
    }
    return "hello world";
}

啟動服務(wù)后,在瀏覽器中請求http://localhost:8080/update,結(jié)果如下:

圖片圖片

結(jié)果與預(yù)期一致!

2.1.2、404 異常特殊處理

默認(rèn)情況下,@ExceptionHandler注解無法捕捉到 404 異常,比如請求一個無效的地址,返回信息如下:

圖片圖片

如果想要捕捉到這種異常,可以在application.properties文件中添加如下配置來實現(xiàn)。

# 如果沒有找到請求地址,拋異常
spring.mvc.throw-exception-if-no-handler-found=true
# 關(guān)閉默認(rèn)的靜態(tài)資源路徑映射
spring.resources.add-mappings=false

啟動服務(wù)后,再次發(fā)起地址請求,結(jié)果如下:

圖片圖片

對于前后端分離開發(fā)的情況,這種方式非常實用;但是如果前后端不分離的項目,比如訪問項目/static目錄下的靜態(tài)資源,可能前端無法正常訪問。

此時,我們可以手動添加資源映射,比如如下操作,前端就能正常訪問靜態(tài)資源了。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 允許訪問localhost:8080/static/目錄下的靜態(tài)資源
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }
}
2.1.3、自定義異常頁面實現(xiàn)

某些場景下,當(dāng)發(fā)生異常時希望跳轉(zhuǎn)到自定義的異常頁面,如何實現(xiàn)呢?

首先,這里基于thymeleaf模板引擎來開發(fā)頁面,在templates目錄下創(chuàng)建一個異常頁面error.html。

<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>錯誤頁面</title>
</head>
<body>
出錯啦,請與管理員聯(lián)系<br>
<span th:text="${message}"></span>
</body>
</html>

然后,修改異常捕捉方法,這里無需添加@ResponseBody注解,示例如下。

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 跳轉(zhuǎn)到異常頁面
     * @param e
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    public ModelAndView exceptionHandler(HttpServletRequest request, Exception e){
        LOGGER.error("未知異常,請求地址:{}, 錯誤信息:{}", request.getRequestURI(), e.getMessage());
        ModelAndView mv = new ModelAndView();
        // 添加錯誤信息對象
        mv.addObject("message", e.getMessage());
        // 要跳轉(zhuǎn)的頁面視圖
        mv.setViewName("error");
        return mv;
    }
}

啟動服務(wù)后,在瀏覽器中再次請求http://localhost:8080/update,結(jié)果如下:

圖片圖片

結(jié)果與預(yù)期一致!

2.1.4、RestControllerAdvice和ControllerAdvice的區(qū)別

很多同學(xué)喜歡用@RestControllerAdvice來代替@ControllerAdvice和@ResponseBody。

例如如下示例!

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 處理所有異常,以json方式響應(yīng)
     * @param e
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    public Object exceptionHandler(HttpServletRequest request, Exception e){
        LOGGER.error("未知異常,請求地址:{}, 錯誤信息:{}", request.getRequestURI(), e.getMessage());
        return ResultMsg.fail(999, e.getMessage());
    }
}

實現(xiàn)效果,與上文等價。

打開@RestControllerAdvice的源碼,你會發(fā)現(xiàn)它將@ControllerAdvice和@ResponseBody注解組合在一起了,因此同時具備兩者效果,部分源碼如下:

package org.springframework.web.bind.annotation;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] assignableTypes() default {};

    Class<? extends Annotation>[] annotations() default {};
}

2.2、全局異常處理方式二

在 Spring Boot 中,除了通過@ControllerAdvice和@ExceptionHandler注解實現(xiàn)全局異常處理外,還有一種通過實現(xiàn)HandlerExceptionResolver接口來完成全局異常的處理。

具體實現(xiàn)示例如下:

@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomExceptionResolver.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
        LOGGER.error("接口請求出現(xiàn)異常,請求地址:{},錯誤信息:{}", request.getRequestURI(), e.getMessage());
        if(e instanceof RuntimeException){
            // 設(shè)置響應(yīng)類型為json格式
            ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
            mv.addObject("code", 500);
            mv.addObject("msg", e.getMessage());
            return mv;
        } else {
            // 設(shè)置響應(yīng)類型為錯誤頁面
            ModelAndView mv = new ModelAndView();
            mv.addObject("message", e.getMessage());
            mv.setViewName("error");
            return mv;
        }
    }
}

當(dāng)出現(xiàn)異常的時候,結(jié)果會以json格式響應(yīng)給客戶端。

啟動服務(wù)后,發(fā)起地址請求,結(jié)果如下:

圖片

這種思路的實現(xiàn)原理,主要是通過 SpringMVC 的異常處理鏈路器來完成異常的全局處理。

SpringMVC 支持用戶自定義異常處理類(需要實現(xiàn)HandlerExceptionResolver),當(dāng)發(fā)生異常時,默認(rèn)異常處理類無法處理時,就會交給自定義異常處理類來完成。實現(xiàn)方面比較靈活,即可以實現(xiàn)以json格式響應(yīng),也可以以頁面視圖的方式響應(yīng)。

雖然這種方式能夠處理全局異常,但是 Spring 官方不推薦使用它;同時實測過程中發(fā)現(xiàn)它無法攔截 404 錯誤,當(dāng)請求錯誤地址時,會優(yōu)先被DefaultHandlerExceptionResolver默認(rèn)異常處理類攔截,自定義的異常處理類無法捕捉。

03、小結(jié)

最后總結(jié)一下,雖然方式一和方式二都可以實現(xiàn)controller層接口請求異常的全局處理,但是在實際使用中,推薦方式一,簡單好維護。

示例代碼地址:

https://gitee.com/pzblogs/spring-boot-example-demo

責(zé)任編輯:武曉燕 來源: 潘志的研發(fā)筆記
相關(guān)推薦

2025-03-11 00:55:00

Spring停機安全

2024-10-18 08:53:49

SpringMybatis微服務(wù)

2019-08-29 14:30:16

代碼開發(fā)工具

2022-10-26 07:14:25

Spring 6Spring業(yè)務(wù)

2021-04-20 10:50:38

Spring Boot代碼Java

2021-09-15 16:20:02

Spring BootFilterJava

2020-08-05 08:30:25

Spring BootJavaSE代碼

2020-03-16 17:20:02

異常處理Spring Boot

2023-01-30 07:41:43

2025-02-07 09:11:04

JSON對象策略

2025-02-19 12:00:00

SpringBootDeepSeekAI

2020-06-18 08:18:35

密碼加密安全

2024-08-01 09:10:03

2024-08-12 10:13:01

2024-12-06 09:27:28

2023-04-17 23:49:09

開發(fā)代碼Java

2011-01-20 10:09:25

2020-04-13 15:25:01

MySQL數(shù)據(jù)庫模糊搜索

2022-04-08 16:27:48

SpringBoot異常處理

2025-02-07 10:52:00

點贊
收藏

51CTO技術(shù)棧公眾號