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

news2024/10/7 13:26:45

【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异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1115955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

教你使用processon快速绘制一张业务流程图?

产品经理在日常工作中经常需要和业务流程图打交道&#xff0c;它能很好地帮助我们梳理业务&#xff0c;高效表达需求&#xff0c;避免做无用功。 对于刚入门的PM来说&#xff0c;对业务流程图完全摸不着头脑的大有人在&#xff0c;今天从业务流程图的基本介绍、分类、业务流程…

【深度学习】Inception模型结构解析,关键词:Inception-v1、v2、v3、v4、Inception-ResNet-v1、Inception-ResNet-v2

目录 1.Inception-v1 1.1 Introduction 1.2 Inception结构 1.3 GoogLeNet 参考文献 2.Inception-v2 2.1 introduction 2.2 BN算法 参考文献 3.Inception-v3 3.1 General Design Principles 3.2 Factorizing Convolutions with Large Filter Size 3.3 其他思想 参…

Spring Security登录账户自定义与数据持久化(5)

1、用户自定义 在前面的案例中&#xff0c;我们的登录用户是基于配置文件来配置的(本质是基于内存)&#xff0c;但是在实际开发中&#xff0c;这种方式肯定是不可取的&#xff0c;在实际项目中&#xff0c;用户信息肯定要存入数据库之中。 Spring Security支持多种用户定义方…

Python如何实现多线程编程

目录 线程的创建 线程的管理 线程的同步 线程池 线程同步和锁 总结 Python是一种广泛使用的编程语言&#xff0c;它具有丰富的库和工具&#xff0c;可以用来实现多线程编程。多线程编程是一种并行计算技术&#xff0c;它通过将任务划分为多个独立的任务并利用多个线程同时…

unity脚本_碰撞检测函数 c#

在项目创建一个脚本文件包新建脚本Cor 将以下代码复制 using UnityEngine; public class Cor : MonoBehaviour{ #region 碰撞检测函数 #endregion //至少一个刚体和两个碰撞器让两个游戏物体产生碰撞 //物理材质Phy Material让两个游戏物体之间表现不同效果 //…

英语——分享篇——每日200词——2201-2400

2201——retreat——[rɪtrɪ:t]——vi.退却&#xff0c;后退——retreat——re热(拼音)treat款待(熟词)——过分热情的款待使他退却——He retreated hastily back to his car.——他迅速撤回到车里。 2202——gap——[gp]——n.间隔&#xff0c;——map——map地图——地图上…

leetcode:217. 存在重复元素(先排序再比较邻位)

一、题目&#xff1a; 函数原型&#xff1a; bool containsDuplicate(int* nums, int numsSize) 参数分析&#xff1a; nums是传入的数组 numsSize是传入数组的元素个数 二、思路&#xff1a; 根据题意&#xff0c;判断数组中是否存在出现两次以上的元素。可以先将数组排序&…

线性代数的本质笔记

课程来自b站发现的《线性代数的本质》&#xff0c;可以帮助从直觉层面理解线性代数的一些基础概念&#xff0c;以及把一些看似不同的数学概念解释之后&#xff0c;发现其实有内在的关联。 这里只对部分内容做一个记录&#xff0c;完整内容请自行观看视频~ 01-向量究竟是什么 …

如何进行自动化测试,提高测试效率?

作为测试人员&#xff0c;在进行比较大的项目时&#xff0c;使用自动化测试能帮助我们事半功倍地完成测试工作&#xff0c;提高测试效率&#xff0c;缩短开发周期。 Eolink Apikit 为测试工程师提供 API 文档管理、快速接口调试、测试用例管理、及自动化测试等功能。协作测试工…

公司电脑监控软件|管控企业U盘,防止员工利用U盘泄密

德人合科技——电脑监控软件可以通过U盘管理系统管控企业U盘&#xff0c;防止员工利用U盘泄密。 PC访问地址&#xff1a;https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 其具体功能如下&#xff1a; U盘接入管控&#xff1a;单位内电脑能否使用U…

同样是发朋友圈,为什么我发的朋友圈没有效果呢?如何做好朋友圈营销呢?

随着手机的发展&#xff0c;越来越多的营销不再局限在电视、电脑&#xff0c;也开始转往移动即时聊天软件。微信承载大量使用者&#xff0c;因此许多企业将营销的目光移到微信的身上。 但是实际操作才发现&#xff0c;营销过程中出现各种各样的问题&#xff0c;最常见的是投入大…

编程题总结 --- 2018

&#xff08;1&#xff09;输入一串字符串&#xff0c;字符串以“#”结尾&#xff0c;判断输入的字符串中0至9的个数。 #include<iostream>using namespace std;int main(){int sum 0;string s;while(cin >> s){if(s "#") break;int n s.size();for(…

10月8日 Jdbc(1)

jdbc 接口是一个类的父类 java连接数据库, java操作数据库, 把java作为数据库的一个客户端 JDBC是接口&#xff0c;而JDBC驱动才是接口的实现&#xff0c;没有驱动无法完成数据库连接&#xff01;每个数据库厂商都有自己的驱动&#xff0c;用来连接自己公司的数据库。 ​ …

AWS SAA-C03考试知识点整理

S3&#xff1a; 不用于数据库功能 分类&#xff1a; S3 Standard &#xff1a;以便频繁访问 S3 Standard-IA 或 S3 One Zone-IA &#xff1a; 不经常访问的数据 Glacier&#xff1a; 最低的成本归档数据 S3 Intelligent-Tiering智能分层 &#xff1a;存储具有不断变化或未知访问…

网络安全副业如何年入数十万 (如何让你的副业超过主页)

安全从业经历与副业经验画了一张“网络安全之副业有道”的思维导图”&#xff0c;该图随着认知提高&#xff0c;将继续丰富完善&#xff0c;共同交流学习。 任何学习的过程一定是要有正向反馈的&#xff0c;如技能的提升、圈内的名气、输出知识带来的经济收入&#xff0c;等等…

如何使用Inno Setup将可执行文件.exe和它的依赖文件及文件夹打包成一个可以安装的.exe文件

环境: Inno Setup 6.2.2 rustdesk编译文件和依赖 问题描述: 如何使用Inno Setup将可执行文件.exe和它的依赖文件及文件夹打包成一个可以安装的.exe文件 解决方案: 一、创建编译脚本 1.新建脚本 下一步 2.填写程序名称版本等信息 3.设置安装默认目录和运行用户更改 选…

什么是SOI

在芯片制程中&#xff0c;经常会听到“SOI”这个名词。而芯片制造上也通常使用SOI衬底制造集成电路。SOI衬底的独特结构可以大大提高芯片的性能&#xff0c;那么SOI到底是什么&#xff1f;有哪些优点&#xff1f;应用在哪些领域&#xff1f;如何制造&#xff1f; 什么是SOI衬底…

SystemVerilog Assertions应用指南 Chapter 1.14蕴含操作符

1.14蕴含操作符 属性p7有下列特别之处 (1)属性在每一个时钟上升沿寻找序列的有效开始。在这种情况下,它在每个时钟上升沿检查信号“a”是否为高。 (2)如果信号“a”在给定的任何时钟上升沿不为高,检验器将产生一个错误信息。这并不是一个有效的错误信息,因为我…

Next.js和sharp实现占位图片生成工具

占位图片&#xff08;Placeholder Image&#xff09; 是前端开发中常用的工具&#xff0c;用于在网页加载慢或未加载完整的情况下&#xff0c;为图像元素提供占位。但是&#xff0c;有时候我们需要更灵活的方式来生成自定义占位图片以满足特定需求。在这篇博客中&#xff0c;我…

ArGIS Engine专题(14)之GP模型根据导入范围与地图服务相交实现叠置分析

一、结果预览 二、需求简介 前端系统开发时,可能遇到如下场景,如客户给出一个图斑范围,导入到系统中后,需要判断图斑是否与耕地红线等地图服务存在叠加,叠加的面积有多少。虽然arcgis api中提供了相交inserect接口,但只是针对图形几何之间的相交,如何要使用该接口,则需…