欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot中全局異常處理的5種實(shí)現(xiàn)方式小結(jié)

 更新時(shí)間:2025年03月31日 09:59:20   作者:風(fēng)象南  
在實(shí)際開發(fā)中,異常處理是一個(gè)非常重要的環(huán)節(jié),合理的異常處理機(jī)制不僅能提高系統(tǒng)的健壯性,還能大大提升用戶體驗(yàn),下面我們就來(lái)看看SpringBoot中全局異常處理的5種實(shí)現(xiàn)方式吧

前言

在實(shí)際開發(fā)中,異常處理是一個(gè)非常重要的環(huán)節(jié)。合理的異常處理機(jī)制不僅能提高系統(tǒng)的健壯性,還能大大提升用戶體驗(yàn)。本文將詳細(xì)介紹SpringBoot中全局異常處理的幾種實(shí)現(xiàn)方式。

為什么需要全局異常處理?

如果沒(méi)有統(tǒng)一的異常處理機(jī)制,當(dāng)系統(tǒng)發(fā)生異常時(shí),可能會(huì)導(dǎo)致以下問(wèn)題

  • 用戶體驗(yàn)差:用戶可能看到一些技術(shù)性的錯(cuò)誤信息,如堆棧跟蹤
  • 安全隱患:暴露系統(tǒng)內(nèi)部錯(cuò)誤詳情可能會(huì)被攻擊者利用
  • 維護(hù)困難:分散在各處的異常處理代碼增加了維護(hù)難度
  • 響應(yīng)格式不一致:不同接口返回的錯(cuò)誤格式不統(tǒng)一,增加前端處理難度

下面,來(lái)看幾種在SpringBoot中實(shí)現(xiàn)全局異常處理的方式。

方式一:@ControllerAdvice/@RestControllerAdvice + @ExceptionHandler

這是SpringBoot中最常用的全局異常處理方式。

首先,定義一個(gè)統(tǒng)一的返回結(jié)果類:

public class Result<T> {
    private Integer code;
    private String message;
    private T data;

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage("操作成功");
        result.setData(data);
        return result;
    }

    public static <T> Result<T> error(Integer code, String message) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }

    // getter和setter方法省略
}

然后,定義自定義異常:

public class BusinessException extends RuntimeException {
    private Integer code;

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

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

    public Integer getCode() {
        return code;
    }
}

創(chuàng)建全局異常處理類:

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 處理自定義業(yè)務(wù)異常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 處理參數(shù)校驗(yàn)異常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Void> handleValidationException(ConstraintViolationException e) {
        return Result.error(400, "參數(shù)校驗(yàn)失?。? + e.getMessage());
    }

    /**
     * 處理資源找不到異常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<Void> handleNotFoundException(NoHandlerFoundException e) {
        return Result.error(404, "請(qǐng)求的資源不存在");
    }

    /**
     * 處理其他所有未捕獲的異常
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        return Result.error(500, "服務(wù)器內(nèi)部錯(cuò)誤:" + e.getMessage());
    }
}

優(yōu)點(diǎn)

  • 代碼簡(jiǎn)潔清晰,易于維護(hù)
  • 可以針對(duì)不同的異常類型定義不同的處理方法
  • 與Spring MVC結(jié)合緊密,可以獲取請(qǐng)求和響應(yīng)上下文

方式二:實(shí)現(xiàn)HandlerExceptionResolver接口

HandlerExceptionResolver是Spring MVC中用于解析異常的接口,我們可以通過(guò)實(shí)現(xiàn)此接口來(lái)自定義異常處理邏輯。

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public ModelAndView resolveException(HttpServletRequest request, 
                                        HttpServletResponse response, 
                                        Object handler, Exception ex) {
        Result<?> result;
        
        // 處理不同類型的異常
        if (ex instanceof BusinessException) {
            BusinessException businessException = (BusinessException) ex;
            result = Result.error(businessException.getCode(), businessException.getMessage());
        } else {
            result = Result.error(500, "服務(wù)器內(nèi)部錯(cuò)誤:" + ex.getMessage());
        }
        
        // 設(shè)置響應(yīng)類型和狀態(tài)碼
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_OK);
        
        try {
            // 將錯(cuò)誤信息寫入響應(yīng)
            PrintWriter writer = response.getWriter();
            writer.write(objectMapper.writeValueAsString(result));
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 返回空的ModelAndView表示異常已經(jīng)處理完成
        return new ModelAndView();
    }
}

優(yōu)點(diǎn)

  • 可以完全控制異常處理的過(guò)程
  • 可以直接操作HttpServletRequest和HttpServletResponse
  • 適合需要特殊處理的場(chǎng)景

缺點(diǎn)

  • 代碼相對(duì)復(fù)雜
  • 無(wú)法利用Spring MVC的注解優(yōu)勢(shì)

方式三:使用SimpleMappingExceptionResolver

SimpleMappingExceptionResolver是HandlerExceptionResolver的一個(gè)簡(jiǎn)單實(shí)現(xiàn),適用于返回錯(cuò)誤視圖的場(chǎng)景。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.Properties;

@Configuration
public class ExceptionConfig {

    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        
        // 設(shè)置默認(rèn)錯(cuò)誤頁(yè)面
        resolver.setDefaultErrorView("error/default");
        
        // 設(shè)置異常映射
        Properties mappings = new Properties();
        mappings.setProperty(BusinessException.class.getName(), "error/business");
        mappings.setProperty(RuntimeException.class.getName(), "error/runtime");
        resolver.setExceptionMappings(mappings);
        
        // 設(shè)置異常屬性名,默認(rèn)為"exception"
        resolver.setExceptionAttribute("ex");
        
        return resolver;
    }
}

對(duì)應(yīng)的錯(cuò)誤頁(yè)面模板(使用Thymeleaf):

<!-- templates/error/business.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>業(yè)務(wù)錯(cuò)誤</title>
</head>
<body>
    <h1>業(yè)務(wù)錯(cuò)誤</h1>
    <p th:text="${ex.message}">錯(cuò)誤信息</p>
</body>
</html>

優(yōu)點(diǎn)

  • 配置簡(jiǎn)單
  • 適合返回錯(cuò)誤頁(yè)面的場(chǎng)景

缺點(diǎn)

  • 主要適用于返回視圖,不適合RESTful API
  • 靈活性有限

方式四:自定義ErrorController

Spring Boot提供了BasicErrorController來(lái)處理應(yīng)用中的錯(cuò)誤,我們可以通過(guò)繼承或替換它來(lái)自定義錯(cuò)誤處理邏輯。

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomErrorController implements ErrorController {

    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ResponseEntity<Result<Void>> handleError(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        
        Integer statusCode = status.value();
        String message = "未知錯(cuò)誤";
        
        switch (statusCode) {
            case 404:
                message = "請(qǐng)求的資源不存在";
                break;
            case 403:
                message = "沒(méi)有權(quán)限訪問(wèn)該資源";
                break;
            case 500:
                message = "服務(wù)器內(nèi)部錯(cuò)誤";
                break;
            default:
                break;
        }
        
        return new ResponseEntity<>(Result.error(statusCode, message), status);
    }

    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
    public String handleErrorHtml(HttpServletRequest request, Map<String, Object> model) {
        HttpStatus status = getStatus(request);
        
        // 添加錯(cuò)誤信息到模型
        model.put("status", status.value());
        model.put("message", status.getReasonPhrase());
        
        // 返回錯(cuò)誤頁(yè)面
        return "error/error";
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        try {
            return HttpStatus.valueOf(statusCode);
        } catch (Exception ex) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }
}

優(yōu)點(diǎn)

  • 可以同時(shí)處理HTML和JSON響應(yīng)
  • 對(duì)所有未處理的錯(cuò)誤提供統(tǒng)一的處理方式
  • 可以獲取錯(cuò)誤的狀態(tài)碼和詳細(xì)信息

缺點(diǎn)

  • 只能處理已經(jīng)發(fā)生的HTTP錯(cuò)誤,無(wú)法攔截自定義異常
  • 一般作為兜底方案使用

方式五:使用錯(cuò)誤頁(yè)面模板

Spring Boot支持通過(guò)靜態(tài)HTML頁(yè)面或模板來(lái)展示特定狀態(tài)碼的錯(cuò)誤。只需要在templates/error/目錄下創(chuàng)建對(duì)應(yīng)狀態(tài)碼的頁(yè)面即可。

目錄結(jié)構(gòu):

src/
  main/
    resources/
      templates/
        error/
          404.html
          500.html
          error.html  # 默認(rèn)錯(cuò)誤頁(yè)面

例如,404.html的內(nèi)容:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>頁(yè)面不存在</title>
</head>
<body>
    <h1>404 - 頁(yè)面不存在</h1>
    <p>您請(qǐng)求的頁(yè)面不存在,請(qǐng)檢查URL是否正確。</p>
    <a href="/" rel="external nofollow" >返回首頁(yè)</a>
</body>
</html>

優(yōu)點(diǎn)

  • 配置極其簡(jiǎn)單,只需創(chuàng)建對(duì)應(yīng)的頁(yè)面即可
  • 適合簡(jiǎn)單的Web應(yīng)用

缺點(diǎn)

  • 靈活性有限
  • 不適合RESTful API
  • 無(wú)法處理自定義異常

實(shí)戰(zhàn)示例:完整的異常處理體系

下面提供一個(gè)完整的異常處理體系示例,組合了多種方式:

首先,創(chuàng)建異常體系:

// 基礎(chǔ)異常類
public abstract class BaseException extends RuntimeException {
    private final int code;

    public BaseException(int code, String message) {
        super(message);
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}

// 業(yè)務(wù)異常
public class BusinessException extends BaseException {
    public BusinessException(String message) {
        super(400, message);
    }

    public BusinessException(int code, String message) {
        super(code, message);
    }
}

// 系統(tǒng)異常
public class SystemException extends BaseException {
    public SystemException(String message) {
        super(500, message);
    }

    public SystemException(int code, String message) {
        super(code, message);
    }
}

// 權(quán)限異常
public class PermissionException extends BaseException {
    public PermissionException(String message) {
        super(403, message);
    }
}

創(chuàng)建全局異常處理器:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 處理自定義基礎(chǔ)異常
     */
    @ExceptionHandler(BaseException.class)
    public Result<?> handleBaseException(BaseException e) {
        logger.error("業(yè)務(wù)異常:{}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 處理參數(shù)校驗(yàn)異常(@Valid)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        String errorMsg = fieldErrors.stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining(", "));
        logger.error("參數(shù)校驗(yàn)錯(cuò)誤:{}", errorMsg);
        return Result.error(400, "參數(shù)校驗(yàn)錯(cuò)誤: " + errorMsg);
    }

    /**
     * 處理所有其他異常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleException(Exception e) {
        logger.error("系統(tǒng)異常:", e);
        return Result.error(500, "服務(wù)器內(nèi)部錯(cuò)誤,請(qǐng)聯(lián)系管理員");
    }
}

使用示例:

@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/{id}")
    public Result<User> getUser(@PathVariable Long id) {
        if (id <= 0) {
            throw new BusinessException("用戶ID必須大于0");
        }
        
        if (id > 100) {
            throw new SystemException("系統(tǒng)維護(hù)中");
        }
        
        if (id == 10) {
            throw new PermissionException("沒(méi)有權(quán)限查看此用戶");
        }
        
        // 模擬查詢用戶
        User user = new User(id, "用戶" + id, "user" + id + "@example.com");
        return Result.success(user);
    }
}

各方式對(duì)比與使用建議

實(shí)現(xiàn)方式適用場(chǎng)景靈活性復(fù)雜度
@ControllerAdvice + @ExceptionHandlerRESTful API、前后端分離項(xiàng)目
HandlerExceptionResolver需要精細(xì)控制異常處理過(guò)程的場(chǎng)景
SimpleMappingExceptionResolver傳統(tǒng)Web應(yīng)用,需要返回錯(cuò)誤頁(yè)面
自定義ErrorController需要自定義錯(cuò)誤頁(yè)面和錯(cuò)誤響應(yīng)的場(chǎng)景
錯(cuò)誤頁(yè)面模板簡(jiǎn)單的Web應(yīng)用,只需自定義錯(cuò)誤頁(yè)面

建議

  • 對(duì)于RESTful API或前后端分離項(xiàng)目:優(yōu)先選擇@ControllerAdvice/@RestControllerAdvice + @ExceptionHandler方式,它提供了良好的靈活性和簡(jiǎn)潔的代碼結(jié)構(gòu)。

  • 對(duì)于需要返回錯(cuò)誤頁(yè)面的傳統(tǒng)Web應(yīng)用:可以使用SimpleMappingExceptionResolver或錯(cuò)誤頁(yè)面模板方式。

  • 對(duì)于復(fù)雜系統(tǒng):可以組合使用多種方式,例如

    • 使用@ControllerAdvice處理業(yè)務(wù)異常
    • 使用自定義ErrorController處理未捕獲的HTTP錯(cuò)誤
    • 使用錯(cuò)誤頁(yè)面模板提供友好的錯(cuò)誤頁(yè)面

最佳實(shí)踐總結(jié)

  • 分層異常處理:根據(jù)業(yè)務(wù)需求,設(shè)計(jì)合理的異常繼承體系,便于分類處理。

  • 統(tǒng)一返回格式:無(wú)論成功還是失敗,都使用統(tǒng)一的返回格式,便于前端處理。

  • 合理記錄日志:在異常處理中記錄日志,可以幫助排查問(wèn)題。不同級(jí)別的異常使用不同級(jí)別的日志。

  • 區(qū)分開發(fā)和生產(chǎn)環(huán)境:在開發(fā)環(huán)境可以返回詳細(xì)的錯(cuò)誤信息,而在生產(chǎn)環(huán)境則應(yīng)該隱藏敏感信息。

  • 異常分類

    • 業(yè)務(wù)異常:用戶操作引起的可預(yù)期異常
    • 系統(tǒng)異常:系統(tǒng)內(nèi)部錯(cuò)誤
    • 第三方服務(wù)異常:調(diào)用外部服務(wù)失敗等
  • 不要忽略異常:即使是捕獲異常后不需要處理,也應(yīng)該至少記錄日志。

結(jié)語(yǔ)

在Spring Boot應(yīng)用中,全局異常處理是提高系統(tǒng)健壯性和用戶體驗(yàn)的重要環(huán)節(jié)。通過(guò)本文介紹的幾種實(shí)現(xiàn)方式,開發(fā)者可以根據(jù)實(shí)際需求選擇合適的實(shí)現(xiàn)方案。在實(shí)際項(xiàng)目中,往往需要結(jié)合多種方式,構(gòu)建一個(gè)完整的異常處理體系。

以上就是SpringBoot中全局異常處理的5種實(shí)現(xiàn)方式小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot全局異常處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Gateway+Swagger2配置聚合文檔方式

    Gateway+Swagger2配置聚合文檔方式

    這篇文章主要介紹了Gateway+Swagger2配置聚合文檔方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。
    2023-03-03
  • Springcloud Nacos基本操作代碼實(shí)例

    Springcloud Nacos基本操作代碼實(shí)例

    這篇文章主要介紹了Springcloud Nacos基本操作代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Springboot處理CORS跨域請(qǐng)求的三種方法

    Springboot處理CORS跨域請(qǐng)求的三種方法

    這篇文章主要介紹了Springboot處理CORS跨域請(qǐng)求的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java Socket編程(四) 重復(fù)和并發(fā)服務(wù)器

    Java Socket編程(四) 重復(fù)和并發(fā)服務(wù)器

    Java Socket編程(四) 重復(fù)和并發(fā)服務(wù)器...
    2006-12-12
  • maven導(dǎo)入無(wú)法拉取所需依賴的解決方法

    maven導(dǎo)入無(wú)法拉取所需依賴的解決方法

    最近遇到個(gè)問(wèn)題maven導(dǎo)入無(wú)法拉取所需依賴的解決方法,本文就來(lái)詳細(xì)的介紹一下解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • 基于JPA查詢部分字段的相關(guān)事項(xiàng)

    基于JPA查詢部分字段的相關(guān)事項(xiàng)

    這篇文章主要介紹了JPA查詢部分字段的相關(guān)事項(xiàng)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java學(xué)習(xí)之緩沖流的原理詳解

    Java學(xué)習(xí)之緩沖流的原理詳解

    為了提高其數(shù)據(jù)的讀寫效率,Java中又定義了四種緩沖流,分別是:字節(jié)緩沖輸入流、字節(jié)緩沖輸出流、字符緩沖輸入流和字符緩沖輸出流。本文主要來(lái)和大家聊聊這些緩沖流的原理,希望對(duì)大家有所幫助
    2023-01-01
  • Mybatis自定義TypeHandler解決特殊類型轉(zhuǎn)換問(wèn)題詳解

    Mybatis自定義TypeHandler解決特殊類型轉(zhuǎn)換問(wèn)題詳解

    這篇文章主要介紹了Mybatis自定義TypeHandler解決特殊類型轉(zhuǎn)換問(wèn)題詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析

    java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析

    Java 內(nèi)存模型的主要目的是定義程序中各種變量的訪問(wèn)規(guī)則, 關(guān)注在虛擬機(jī)中把變量值存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量值這樣的底層細(xì)節(jié)
    2021-09-09
  • 教你一步到位部署運(yùn)行MyBatis3源碼(保姆級(jí))

    教你一步到位部署運(yùn)行MyBatis3源碼(保姆級(jí))

    一個(gè)框架的運(yùn)行流程從最簡(jiǎn)單的一個(gè)helloworld來(lái)看其源碼就能了解到框架的原理是什么,這篇文章主要給大家介紹了關(guān)于如何一步到位部署運(yùn)行MyBatis3源碼的相關(guān)資料,需要的朋友可以參考下
    2022-06-06

最新評(píng)論