Spring MVC统一异常处理

导读:本篇文章讲解 Spring MVC统一异常处理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Spring MVC统一异常处理

一、为什么需要统一异常处理?

1.1 try catch带来的问题

  • 平时项目中处理Controller层异常很多时候都会使用try catch去捕获异常(包括Controller层中调用的Service层中的try catch)。

  • 这样处理异常,会造成代码中带有大量的try catch块,不但代码不美观不优雅,严重的还会造成很大的性能问题。

  • 这时候可能有人会说用SpringAOP定义切面去处理所有Controller,然后有异常的时候统一拦截起来。

  • 但是这种呆瓜操作,Spring MVC早就给我们想好了,无需我们自己取定义。

1.2 最佳解决方案

Spring MVC给我们提供了一个@RestControllerAdvice来增强所有@RestController,然后使用@ExceptionHandler注解,就可以拦截到对应的异常。

二、使用Spring MVC的统一异常处理机制

2.1 和前端约定好的通用返回结果类

这一步是非必须,但是项目中一般都会约定好一个通用的消息返回格式。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

/**
 * 通用返回结果类
 * @param <T>    通用泛型(传了这个泛型的话, 数据data的类型就和这个泛型一致)
 *               这个T可以理解为type类型。
 *               使用其它字母也行,但是最好能保持一定的语义化,让大家更容易理解。
 */
@Data                          //lombok注解 提供get/set、toString、equal等实体类都有的方法
@NoArgsConstructor             //lombok注解 提供无参构造
@AllArgsConstructor            //lombok注解 提供全参构造
public class ReturnMessage<T> implements Serializable {

    private Integer code;    //返回状态码
    private String msg;      //错误信息
    private T data;          //数据
    
    /**
     * 创建一个要返回的消息对象
     * @param code  状态码
     * @param msg   错误信息
     * @param data  数据
     * @return
     * @param <T>   声明这个静态方法是一个泛型方法
     */
    public static <T> ReturnMessage<T> create(Integer code, String msg, T data) {
        ReturnMessage<T> r = new ReturnMessage<T>(code, msg, data);
        return r;
    }

}

2.2 定义响应码枚举类

项目中通常会约定好返回给前端的状态码,为了代码得优雅和可维护性,可以使用一个枚举类来管理

import lombok.Getter;

@Getter	 //lombok注解 提供各属性的get方法
public enum AppCode {

    SUCCESS(200, "成功"),
    SERVICE_ERROR(500, "服务器异常"),
	PASSWORD_ERROR(5001, "密码错误"),
    CHECK_ERROR(5002, "验证码无效");
        
    private int code;
    private String msg;

    AppCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

2.3 自定义业务异常类

import com.tanhua.model.vo.ErrorResult;
import lombok.Data;

@Data
public class BusinessException extends RuntimeException{

    private AppCode appCode;		//异常状态码对象

    public BusinessException(AppCode appCode) {
        super(appCode.getMsg());
        this.appCode = appCode;
    }
}

2.4 自定义统一异常处理类

import com.tanhua.model.vo.ErrorResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 *  自定义统一异常处理类
 *	步骤:
 *  	1、通过注解,声明异常处理类
 *  	2、编写方法,在方法内部处理异常,构造响应数据
 *  	3、方法上编写注解@ExceptionHandler,指定此方法可以处理的异常类型
 *
 *	使用Spring推荐的ResponseEntity作为返回值时:
 *		1. 可以手动修改返回的状态码和响应体
 *		2. 此类不需要加@ResponseBody
 *
 *	如果是自定义的返回结果类作为返回值时:
 *		1.比如直接使用上面的ReturnMessage作为返回值, 则需要在此类或者方法上添加@ResponseBody
 *	    2.此时可以使用@RestControllerAdvice注解, 它等同于ControllerAdvice + @ResponseBody
 */

@ControllerAdvice
public class ExceptionAdvice {

    //处理业务异常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity handlerBusinessException(BusinessException be) {
        be.printStackTrace();
        AppCode appCode = be.getAppCode();
        ReturnMessage returnMessage = new ReturnMessage(
                                                        appCode.getCode(),
                                                        appCode.getMsg(),
                                                        null);
		return ResponseEntity.status(appCode.getCode())
            				 .body(returnMessage);
    }

    //处理不可预知的异常 (像什么空指针、数组下标越界、类型转换等非业务异常)
    @ExceptionHandler(Exception.class)
    public ResponseEntity handlerException(Exception e) {
        e.printStackTrace();      
        ReturnMessage returnMessage = new ReturnMessage(
                                                        AppCode.SERVICE_ERROR.getCode(),
                                                        e.getMessage(),
                                                        null);
        /**
         *   拓展:
         *   HttpStatus 是Spring自带的状态码信息类
         *   源码中的定义 :
         *           INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
         *   可以看到 INTERNAL_SERVER_ERROR代表的就是500状态码,服务端异常
         *   这种写法语义性更强,且不用自己再去定义
         *    
         * 	 但是我这里并没有使用,而是选择了自定义状态码
        */
        return ResponseEntity.status(AppCode.SERVICE_ERROR.getCode())
            				 .body(returnMessage);
    }

}

2.5 遇到业务异常时的处理示例:

public Boolean loginVerification(String phone, String password) {
	User user = userService.findByPhone(phone);
    if(user.getPassword != password) {
		//密码不相等,抛出自定义业务异常
        throw new BusinessException(ErrorResult.passswordError());
        return false;
    }
    return true;
}

2.6 统一异常异常执行原理

  1. 定义好了统一异常处理,再遇到业务异常,可以直接throw new BusinessException()的方式抛出异常

  2. 此时我们定义的ExceptionAdvice类就会根据handlerBusinessException方法去构建错误信息返回给前端。

  3. 而其它项目中的空指针、数组下标越界、类型转换等非业务异常,交由handlerException处理。

    这样就实现了项目中的统一异常处理。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116508.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!