如何优雅的进行Controller全局异常处理?

【Spring Boot系列】 如何优雅的进行Controller全局异常处理?

【Spring Boot系列】 如何优雅的进行Controller全局异常处理?

在进入正题之前,先给大家看一段代码,如下所示。

package com.panda.handle_try_catch_gracefully.controller;

import com.panda.handle_try_catch_gracefully.common.Result;
import com.panda.handle_try_catch_gracefully.domain.po.User;
import com.panda.handle_try_catch_gracefully.domain.vo.UserVO;
import com.panda.handle_try_catch_gracefully.enums.ExceptionEnum;
import com.panda.handle_try_catch_gracefully.exceptions.BusinessException;
import com.panda.handle_try_catch_gracefully.service.IUserService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("ori/user")
public class OriginUserController {
    @Resource
    private IUserService userService;

    @GetMapping("getUserInfo")
    public Result<UserVO> getUserInfo(String userId) {
        try {
            return userService.getUserInfo(userId);
        } catch (Exception e) {
            throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);
        }
    }

    @PostMapping("listUserInfo")
    public Result<List<UserVO>> listUserInfo(UserVO query) {
        try {
            return userService.listUserInfo(query);
        } catch (Exception e) {
            throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);
        }
    }

    @PostMapping("saveUser")
    public Result<String> saveUser(User user) {
        try {
            return userService.saveUser(user);
        } catch (Exception e) {
            throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);
        }
    }

    @PostMapping("updateUser")
    public Result<Boolean> updateUser(User user) {
        try {
            return userService.updateUser(user);
        } catch (Exception e) {
            throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);
        }
    }

    @PostMapping("deleteUser")
    public Result<Boolean> deleteUser(@RequestParam("userId") String userId) {
        try {
            return userService.deleteUser(userId);
        } catch (Exception e) {
            throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);
        }
    }
}

不知道大家在项目中是否遇到过上面这种情况——controller里满屏的try-catch代码块,真的是闪瞎了我的钛合金狗眼呀。

虽然丑陋,但是这些try-catch代码块还是起到了一定的作用的——给前端响应一些通俗易懂的提示信息,如“用户编码不能为空”、“保存用户信息异常”、“更新用户信息失败”等。

如果没有这些丑陋的try-catch代码块,一旦抛出异常,用户看到的可能就是类似

Exception in thread "main" java.lang.ArithmeticException: / by zero

Exception in thread "main" java.lang.NullPointerException

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 2 out of bounds for length 0

之类的错误信息,对用户来说,无异于天书,极大地降低了用户体验度。

try-catch:虽然我长得丑,但是我有用呀!

这当然是我们不能容忍的!

作为一个有着强迫症的程序员,怎么能容忍这样的代码出现在我的项目中呢!

外行看热闹,可能看不出上面这段代码有什么丑陋的,但是,内行看门道,一眼就可以看出上面代码的丑陋之处。说一千道一万,上面这段代码究竟丑陋在哪里呢?

上面也提到了,try-catch代码块满屏飞,且处理逻辑一致,都是捕获异常,然后抛出异常或者返回错误信息,但这并不符合代码的可重用原则。

上面的代码只是一个controller,一个项目中可能有几十上百,甚至更多个controller呢!因此,对这块代码的主要优化思路,就是找出一种方法代替多次出现的try-catch代码块,并且原有的功能不能缺失

这种方法就是本文要讲的内容——全局异常处理!

全局异常处理

所谓全局异常处理,也叫统一异常处理,是一种统一处理异常的思路。

这种方法的好处在于,只需要在一个地方处理异常逻辑,就可以将controller的异常给捕获掉,而不用我们在每个controller类中写重复且丑陋的try-catch代码块,来捕获异常。

Spring Boot 的全局异常处理有两个很重要的注解,一个是ControllerAdvice注解或者RestControllerAdvice注解,另一个是ExceptionHandler注解。

ControllerAdvice注解或者RestControllerAdvice注解在类上使用,表示开启全局异常的捕获

ExceptionHandler注解在方法上使用,可以通过value属性指定一个或多个异常,并捕获指定的异常,一般在方法体内对捕获到的异常进行解析,然后进行输出或返回等操作

ControllerAdvice注解

简介

ControllerAdvice注解是Spring 3.2中新增的一个注解,是Controller的增强器,它的作用是给Controller(控制器)添加统一的操作或处理。

ControllerAdvice注解最常见的使用场景就是,结合ExceptionHandler注解用于全局异常处理。

ControllerAdvice注解是在类上声明的注解,用法主要有以下三点:

全局异常处理

结合ExceptionHandler注解,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

全局数据预处理

结合InitBinder注解,用于对请求参数预处理,将表单中的参数绑定到实体上,或者对日期、金额类参数进行格式转换等。

全局数据绑定

结合ModelAttribute注解,将方法参数或方法返回值绑定到命名模型属性,该属性向web视图公开。

属性

basePackages

该属性可以指定一个或多个包路径,这些包及其子包下的所有Controller都被ControllerAdvice注解管理。

@RestControllerAdvice(basePackages={"com.panda","com.cat"})
public class GlobalExceptionHandler {    
    @ExceptionHandler(Exception.class)    
    public String handleException(Exception e) {    
        return "error";
    }   
} 
basePackageClasses

该属性的作用和 basePackages 差不多。

该属性可以指定一个或多个Controller类,这些类所属的包及其子包下的所有 Controller 都被该ControllerAdvice注解管理。

@RestControllerAdvice(basePackageClasses={UserController.class, OrderController.class})
public class GlobalExceptionHandler {    
    @ExceptionHandler(Exception.class)    
    public String handleException(Exception e) {    
        return "error";
    }   
} 
assignableTypes

该属性指定一个或多个 Controller 类,这些类被该ControllerAdvice注解管理。

@RestControllerAdvice(assignableTypes={UserController.class, OrderController.class})
public class GlobalExceptionHandler {    
    @ExceptionHandler(Exception.class)    
    public String handleException(Exception e) {    
        return "error";
    }   
} 
annotations

该属性指定一个或多个注解,被这些注解所标记的Controller会被该ControllerAdvice注解管理。

@RestControllerAdvice(annotations = {UserAnnotation.class, OrderAnnotation.class})
public class GlobalExceptionHandler {    
    @ExceptionHandler(Exception.class)    
    public String handleException(Exception e) {    
        return "error";
    }   
} 

RestControllerAdvice注解

简介

RestControllerAdvice注解是一个组合注解,由ControllerAdvice注解和ResponseBody注解组成。可见其作用和ControllerAdvice注解差不多。

RestControllerAdvice注解是Spring 4.3中新增的一个注解,也是Controller的增强器,它的作用同样是给Controller(控制器)添加统一的操作或处理。

RestControllerAdvice注解和ControllerAdvice注解的区别

1、当我们自定义的全局异常处理类加上ControllerAdvice注解时,如果异常处理方法需要返回json数据,则需要给每个异常处理方法添加ResponseBody注解。

2、当我们自定义的全局异常处理类加上RestControllerAdvice注解时,异常处理方法自动返回JSON格式的数据,我们不需要在该方法上再添加ResponseBody注解。

属性

该注解的属性和ControllerAdvice注解的属性相同,不再赘述。

ExceptionHandler注解

简介

ExceptionHandler注解用来统一处理方法抛出的异常。

被ExceptionHandler注解标注的方法支持以下参数类型

  1. 异常参数,如Exception.class、RuntimeException等。
  2. 请求和/或响应对象(通常来自Servlet API),例如javax.servlet.ServletRequest 或者 javax.servlet.http.HttpServletRequest。
  3. session对象,如javax.servlet.http.HttpSession。
  4. org.springframework.web.context.request.WebRequest 或者 org.springframework.web.context.request.NativeWebRequest。
  5. 表示当前请求区域设置的的java.util.Locale对象,例如org.springframework.web.servlet.LocaleResolver。
  6. java.io.InputStream 或者 java.io.Reader。
  7. java.io.OutputStream 或者 java.io.Writer。
  8. org.springframework.ui.Model。

被ExceptionHandler注解标注的方法支持以下返回类型

  1. ModelAndView类型。
  2. org.springframework.ui.Model类型。
  3. java.util.Map类型。
  4. org.springframework.web.servlet.View类型。
  5. String类型。
  6. ResponseBody注解方法(仅限Servlet)以设置响应内容。
  7. HttpEntity<?> 或者 ResponseEntity<?> 类型。
  8. void类型。

属性

value

指定需要处理的一个或者多个异常类。

如果为空,则默认处理异常处理方法参数列表中列出的所有异常。

@ExceptionHandler(value = BusinessException.class)
public <T> Result<T> businessExceptionHandler(BusinessException businessException) {
    log.error(businessException.getErrorMsg(), businessException);
    return Result.fail(businessException.getCode(), businessException.getErrorMsg());
}

@ExceptionHandler
public <T> Result<T> exceptionHandler1(Exception exception) {
    log.error(exception.getMessage());
    ExceptionEnum exceptionEnum = ExceptionEnum.UNKNOWN;
    if (exception instanceof BusinessException) {
        exceptionEnum = ExceptionEnum.INTERNAL_SERVER_ERROR;
    }
    if (exception instanceof IndexOutOfBoundsException) {
        exceptionEnum = ExceptionEnum.ILLEGAL_ARGUMENT_ERROR;
    }
    return Result.fail(exceptionEnum);
}

代码示例

搭建Spring Boot项目的过程就不在赘述了,项目结构如下所示。

POM.xml代码

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mykits</artifactId>
        <groupId>com.panda</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>handle-try-catch-gracefully</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
    </dependencies>
</project>

UserController.java代码

controller类,和上面的OriginUserController相比,代码量少了很多——try-catch代码块消失了。

放眼望去,整体代码清爽不少,核心代码尽收眼底。

package com.panda.handle_try_catch_gracefully.controller;

import com.panda.handle_try_catch_gracefully.common.Result;
import com.panda.handle_try_catch_gracefully.domain.po.User;
import com.panda.handle_try_catch_gracefully.domain.vo.UserVO;
import com.panda.handle_try_catch_gracefully.service.IUserService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {

    @Resource
    private IUserService userService;

    @GetMapping("getUserInfo")
    public Result<UserVO> getUserInfo(String userId) {
        return userService.getUserInfo(userId);
    }

    @PostMapping("listUserInfo")
    public Result<List<UserVO>> listUserInfo(UserVO query) {
        return userService.listUserInfo(query);
    }

    @PostMapping("saveUser")
    public Result<String> saveUser(User user) {
        return userService.saveUser(user);
    }

    @PostMapping("updateUser")
    public Result<Boolean> updateUser(User user) {
        return userService.updateUser(user);
    }

    @PostMapping("deleteUser")
    public Result<Boolean> deleteUser(@RequestParam("userId") String userId) {
        return userService.deleteUser(userId);
    }
}

Result.java代码

封装的统一返回结果。

code:响应结果代码,在本文中,可以是自定义的代码,也可以是ExceptionEnum枚举的code。

msg:错误信息。主要在方法调用失败时,返回给前端的提示信息。当然在方法调用成功时,也可以返回给前端一个类似“请求成功”之类的信息。

successFlag:响应成功与否的标志。true表示成功,false表示失败。

data:返回给前端的数据。一般只有在响应成功时才会向前端返回数据,以查询类方法居多。

package com.panda.handle_try_catch_gracefully.common;

import com.panda.handle_try_catch_gracefully.enums.ExceptionEnum;

public class Result<T> {
    private String code;

    private String msg;

    private Boolean successFlag;

    private T data;

    public static <T> Result<T> success() {
        Result<T> result = new Result<>();
        result.successFlag(true);
        return result;
    }

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.successFlag(true).data(data);
        return result;
    }

    public static <T> Result<T> fail(String code, String errorMsg) {
        Result<T> result = new Result<>();
        result.successFlag(false).code(code).msg(errorMsg);
        return result;
    }

    public static <T> Result<T> fail(ExceptionEnum exceptionEnum) {
        Result<T> result = new Result<>();
        result.successFlag(false).code(exceptionEnum.getCode()).msg(exceptionEnum.getErrorMsg());
        return result;
    }

    public Result<T> code(String code) {
        this.code = code;
        return this;
    }

    public Result<T> msg(String msg) {
        this.msg = msg;
        return this;
    }

    public Result<T> successFlag(Boolean successFlag) {
        this.successFlag = successFlag;
        return this;
    }

    public Result<T> data(T data) {
        this.data = data;
        return this;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public T getData() {
        return data;
    }

    public Boolean getSuccessFlag() {
        return successFlag;
    }
}

User.java代码

用户信息实体类。

在实际项目中,User的各个属性一般对应用户表的各个字段。

package com.panda.handle_try_catch_gracefully.domain.po;

import lombok.Data;

import java.util.Date;

@Data
public class User {

    private String id;

    private String name;

    private String mobilePhone;

    private Date createTime;

    private String createBy;

    private Date updateTime;

    private String updateBy;

    private Integer validFlag;

    private Integer deleteFlag;
}

UserVO.java代码

用户信息。

返回给前端的实体类。

在本文中,为了省事,也作为查询条件。在实际项目中,应该和查询条件区分开,用类似UserQuery之类的实体表示用户查询条件。

package com.panda.handle_try_catch_gracefully.domain.vo;

import com.panda.handle_try_catch_gracefully.domain.po.User;
import lombok.Data;

@Data
public class UserVO extends User {
    
}

ExceptionEnum.java代码

异常枚举类。

code:异常代码。

errorMsg:异常提示信息。

package com.panda.handle_try_catch_gracefully.enums;

public enum ExceptionEnum {
    /**
     * 请求错误!
     */
    BAD_REQUEST("400", "请求错误!"),
    /**
     * 未经授权的请求!
     */
    UNAUTHORIZED("401", "未经授权的请求!"),
    /**
     * 没有访问权限!
     */
    FORBIDDEN("403", "没有访问权限!"),
    /**
     * 请求的资源未不到!
     */
    NOT_FOUND("404", "请求的资源未不到!"),
    /**
     * 服务器内部错误!
     */
    INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
    /**
     * 服务器正忙,请稍后再试!
     */
    BAD_GATEWAY("502", "服务器正忙,请稍后再试!"),
    /**
     * 服务器正忙,请稍后再试!
     */
    SERVICE_UNAVAILABLE("503", "服务器正忙,请稍后再试!"),
    /**
     * 网关超时!
     */
    GATEWAY_TIMEOUT("504", "网关超时!"),
    /**
     * 非法参数异常!
     */
    ILLEGAL_ARGUMENT_ERROR("10000", "非法参数异常!"),
    /**
     * 用户ID不能为空!
     */
    USER_ID_NOT_BLANK("10001", "用户ID不能为空!"),
    /**
     *
     */
    UNKNOWN("9999", "未知异常!");

    /**
     * 错误码
     */
    private final String code;

    /**
     * 错误描述
     */
    private final String errorMsg;

    ExceptionEnum(String code, String errorMsg) {
        this.code = code;
        this.errorMsg = errorMsg;
    }

    public String getCode() {
        return code;
    }

    public String getErrorMsg() {
        return errorMsg;
    }
}

BusinessException.java代码

在实际项目中,推荐使用自定义的业务异常类,而不用RuntimeException。

如果代码中抛出了RuntimeException,IDEA会提示上图之类的信息(可能要安装插件)。

package com.panda.handle_try_catch_gracefully.exceptions;

import com.panda.handle_try_catch_gracefully.enums.ExceptionEnum;

public class BusinessException extends RuntimeException {
    /**
     * 异常枚举
     */
    private ExceptionEnum exceptionEnum;
    /**
     * 错误码
     */
    private final String code;
    /**
     * 错误信息
     */
    private final String errorMsg;

    public BusinessException(ExceptionEnum exceptionEnum) {
        super(String.format("code = %s, errorMsg = %s", exceptionEnum.getCode(), exceptionEnum.getErrorMsg()));
        this.exceptionEnum = exceptionEnum;
        this.code = exceptionEnum.getCode();
        this.errorMsg = exceptionEnum.getErrorMsg();
    }

    public BusinessException(String code, String errorMsg) {
        super(String.format("code = %s, errorMsg = %s", code, errorMsg));
        this.code = code;
        this.errorMsg = errorMsg;
    }

    public BusinessException(String code, String errorMsg, Object... args) {
        super("code = " + code + ", errorMsg = " + String.format(errorMsg, args));
        this.code = code;
        this.errorMsg = String.format(errorMsg, args);
    }

    public ExceptionEnum getExceptionEnum() {
        return exceptionEnum;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public String getCode() {
        return code;
    }

}

UnifiedExceptionHandler.java代码

统一异常处理类。

注意@RestControllerAdvice注解。

在本类中,我们创建三个方法,分别处理不同场景的异常。

businessExceptionHandler方法处理抛出BusinessException异常的场景,即一旦抛出BusinessException异常,就会被businessExceptionHandler方法处理。

illegalArgumentExceptionHandler方法处理抛出IllegalArgumentException异常的场景,即一旦抛出IllegalArgumentException异常,就会被illegalArgumentExceptionHandler方法处理。

exceptionHandler方法是兜底的,捕获抛出Exception异常的场景,即一旦抛出Exception异常,就会被exceptionHandler方法处理。

当然,你可以根据实际项目需要,创建更多的异常处理方法,如空指针异常处理方法、数组越界异常处理方法、类找不到异常处理方法等。

package com.panda.handle_try_catch_gracefully.handler;

import com.panda.handle_try_catch_gracefully.common.Result;
import com.panda.handle_try_catch_gracefully.enums.ExceptionEnum;
import com.panda.handle_try_catch_gracefully.exceptions.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class UnifiedExceptionHandler {
    /**
     * 业务异常处理
     *
     * @param businessException 业务异常信息
     */
    @ExceptionHandler(value = BusinessException.class)
    public <T> Result<T> businessExceptionHandler(BusinessException businessException) {
        log.error(businessException.getErrorMsg(), businessException);
        return Result.fail(businessException.getCode(), businessException.getErrorMsg());
    }

    /**
     * 未知异常处理
     *
     * @param exception 异常信息
     */
    @ExceptionHandler(value = Exception.class)
    public <T> Result<T> exceptionHandler(Exception exception) {
        log.error(exception.getMessage(), exception);
        return Result.fail(ExceptionEnum.UNKNOWN.getCode(), ExceptionEnum.UNKNOWN.getErrorMsg());
    }

    /**
     * 参数异常处理
     *
     * @param exception 异常信息
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    public <T> Result<T> illegalArgumentExceptionHandler(IllegalArgumentException exception) {
        log.error(exception.getMessage(), exception);
        return Result.fail(ExceptionEnum.ILLEGAL_ARGUMENT_ERROR.getCode(), ExceptionEnum.ILLEGAL_ARGUMENT_ERROR.getErrorMsg());
    }
}

IUserService.java代码

用户接口类。

在本例中定义了5个方法:

getUserInfo:根据用户ID查询用户信息。

listUserInfo:根据查询条件查询符合条件用户列表。

saveUser:保存用户信息。

updateUser:更新用户信息。

deleteUser:删除用户信息。

package com.panda.handle_try_catch_gracefully.service;

import com.panda.handle_try_catch_gracefully.common.Result;
import com.panda.handle_try_catch_gracefully.domain.po.User;
import com.panda.handle_try_catch_gracefully.domain.vo.UserVO;

import java.util.List;

public interface IUserService {

    Result<UserVO> getUserInfo(String userId);

    Result<List<UserVO>> listUserInfo(UserVO query);

    Result<String> saveUser(User user);

    Result<Boolean> updateUser(User user);

    Result<Boolean> deleteUser(String userId);
}

UserServiceImpl.java代码

用户接口实现类。

由于本文的重点不是实现业务逻辑,因此各个实现方法并没有详细的业务逻辑,而是抛出不同的异常,验证我们上面给出统一异常处理类是否可以真的处理各种异常。

package com.panda.handle_try_catch_gracefully.service.impl;

import com.panda.handle_try_catch_gracefully.common.Result;
import com.panda.handle_try_catch_gracefully.domain.po.User;
import com.panda.handle_try_catch_gracefully.domain.vo.UserVO;
import com.panda.handle_try_catch_gracefully.service.IUserService;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements IUserService {

    @Override
    public Result<UserVO> getUserInfo(String userId) {
        // 默认抛出异常
        throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);
    }

    @Override
    public Result<List<UserVO>> listUserInfo(UserVO query) {
        // 默认抛出异常
        throw new IllegalArgumentException();
    }

    @Override
    public Result<String> saveUser(User user) {
        // 默认抛出异常
        throw new IndexOutOfBoundsException();
    }

    @Override
    public Result<Boolean> updateUser(User user) {
        // 默认抛出异常
        throw new RuntimeException();
    }

    @Override
    public Result<Boolean> deleteUser(String userId) {
        // 默认抛出异常
        throw new ClassCastException();
    }
}

测试

1、service方法抛出BusinessException异常

测试方法

http://localhost:8080/user/getUserInfo

测试结果
{
  "code": "500",
  "msg": "服务器内部错误!",
  "successFlag": false,
  "data": null
}

从上面的代码可知,getUserInfo方法抛出的异常如下:

throw new BusinessException(ExceptionEnum.INTERNAL_SERVER_ERROR);

恰好可以被UnifiedExceptionHandler的businessExceptionHandler方法监听。

测试结论

测试成功。

统一异常处理类可以正常捕获BusinessException异常。

2、service方法抛出IllegalArgumentException异常

测试方法

http://localhost:8080/user/listUserInfo

测试结果
{
  "code": "10000",
  "msg": "非法参数异常!",
  "successFlag": false,
  "data": null
}

从上面的代码可知,listUserInfo方法抛出的异常如下:

throw new IllegalArgumentException();

恰好可以被UnifiedExceptionHandler的illegalArgumentExceptionHandler方法监听。

测试结论

测试成功。

统一异常处理类可以正常捕获IllegalArgumentException异常。

3、service方法抛出IndexOutOfBoundsException异常

测试方法

http://localhost:8080/user/saveUser

测试结果
{
  "code": "500",
  "msg": "服务器内部错误!",
  "successFlag": false,
  "data": null
}

从上面的代码可知,saveUser方法抛出的异常如下:

throw new IndexOutOfBoundsException();

虽然UnifiedExceptionHandler没有专门处理IndexOutOfBoundsException异常的方法,但是有一个兜底的exceptionHandler方法,该方法可以监听Exception及其子类的异常类型。

而IndexOutOfBoundsException异常恰好是Exception的子类,因此可以被正常监听到。

测试结论

测试成功。

统一异常处理类可以正常捕获IndexOutOfBoundsException异常。

4、service方法抛出RuntimeException异常

测试方法

http://localhost:8080/user/updateUser

测试结果
{
  "code": "9999",
  "msg": "未知异常!",
  "successFlag": false,
  "data": null
}

从上面的代码可知,updateUser方法抛出的异常如下:

throw new RuntimeException();

同上。

虽然UnifiedExceptionHandler没有专门处理RuntimeException异常的方法,但是有一个兜底的exceptionHandler方法,该方法可以监听Exception及其子类的异常类型。

而RuntimeException异常恰好是Exception的子类,因此可以被正常监听到。

测试结论

测试成功。

统一异常处理类可以正常捕获RuntimeException异常。

5、service方法抛出ClassCastException异常

测试方法

http://localhost:8080/user/deleteUser

测试结果
{
  "code": "9999",
  "msg": "未知异常!",
  "successFlag": false,
  "data": null
}

从上面的代码可知,deleteUser方法抛出的异常如下:

throw new ClassCastException();

同上。

虽然UnifiedExceptionHandler没有专门处理ClassCastException异常的方法,但是有一个兜底的exceptionHandler方法,该方法可以监听Exception及其子类的异常类型。

而ClassCastException异常恰好是Exception的子类,因此可以被正常监听到。

测试结论

测试成功。

统一异常处理类可以正常捕获ClassCastException异常。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
@Path注解是JAX-RS规范中的注解,而Spring Boot是基于Spring框架的Web应用程序框架,两者并不是直接相关的。在Spring Boot中,通常使用@ControllerAdvice注解来定义全局异常处理器,它可以捕获所有Controller中抛出的异常,并根据异常类型或其他条件进行处理。 如果您的应用程序同时使用JAX-RS和Spring Boot,您可以使用Spring Boot的@ControllerAdvice注解来定义全局异常处理器,但是您需要使用JAX-RS的ExceptionMapper将JAX-RS的异常映射到Spring Boot的异常处理器中。 例如,您可以创建一个类并使用@Provider注解将它标记为JAX-RS提供程序,并实现ExceptionMapper接口,将JAX-RS的异常映射到Spring Boot的异常处理器中,如下所示: ``` @Provider public class MyExceptionMapper implements ExceptionMapper<Exception> { @Override public Response toResponse(Exception exception) { // 将JAX-RS的异常映射到Spring Boot的异常处理器中 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal server error").build(); } } ``` 然后,您可以使用@ControllerAdvice注解来定义Spring Boot的全局异常处理器,并处理所有异常,例如: ``` @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException(Exception ex) { ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } } ``` 需要注意的是,JAX-RS和Spring Boot的异常处理器是两个不同的机制,需要通过ExceptionMapper将它们连接起来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尘世中-迷途小书童

欢迎IT从业者的头脑风暴

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值