关于Spring统一异常处理及说明

目录
  • 相关注解概述
  • 全局异常依赖包
  • 示例
  • @ExceptionHandler注解使用方法
    • 基本使用方法
    • 注解的参数
    • 异常类型就近原则
    • 注解方法的返回值
    • 注解使用错误举例

相关注解概述

通过使用@RestControllerAdvice或@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义;

  • @RestControllerAdvice:返回Json格式数据;
  • @ControllerAdvice:返回视图类型数据;
  • @ResponseBody:和Controller方法上的用法一样,会将方法中返回值转换成Json格式后返回给客户端;
  • @ExceptionHandler:用来定义函数针对的异常类型,最后将Exception对象和请求URL映射到返回结果中;用于注释异常处理类,value属性指定需要拦截的异常类型。
  • spring应用启动后,被@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法,都会作用在被@RequestMapping注解的方法上。
  • 全局捕获异常的原理:使用AOP切面技术。
  • 用@RequestBody,就解决了JSon自动绑定。

全局异常依赖包

由于全局捕获异常使用的是AOP切面技术,需要直接或间接导入aop的jar包。

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

示例

import com.terse.develop.utils.exception.AdminException;
import com.terse.develop.utils.exception.MsgResultException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.net.ConnectException;
import java.util.List;
import java.util.Set;

/**
 * 全局异常处理
 */
@RestControllerAdvice
@ResponseBody
public class ExceptionHandlerController {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlerController1.class);

    public static void print(Exception ex) {
        logger.error(ex.toString());
        ex.printStackTrace();
    }

    //Http请求方式不支持
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Exception HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        print(e);
        return new Exception("Http请求方式不支持");
    }

    /**
     * MsgResultException、AdminException 均为自定义异常类
     */
    @ExceptionHandler(value = {MsgResultException.class, AdminException.class})
    public Exception exception(Exception e) {
        print(e);
        if (e instanceof MsgResultException)
            //捕获什么异常信息就返回给前端什么信息
            return new MsgResultException(((MsgResultException) e).getMsg());
        else
            //只返回给后端"请重试"
            return new AdminException("请重试");
    }

    //request Body 读取异常
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public Exception httpMessageNotReadableException(HttpMessageNotReadableException e) {
        print(e);
        return new Exception("请检查数据是否正确");
    }

    //参数类型转换异常
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public Exception getNumberException(MethodArgumentTypeMismatchException e) {
        print(e);
        return new Exception("String类型不能转换成数字类型");
    }

    /**
     * 校验基本类型
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public Exception ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        String msg = null;
        for (ConstraintViolation<?> cvl : constraintViolations) {
            msg = cvl.getMessageTemplate();
        }
        logger.error(msg);
        return new Exception(msg);
    }

    //缺少参数异常
    @ExceptionHandler(value = ServletRequestBindingException.class)
    public Exception ServletRequestBindingException(ServletRequestBindingException e) {
        print(e);
        return new Exception("缺少参数");
    }

    /**
     * 校验对象类型
     */
    @ExceptionHandler(value = BindException.class)
    public Exception BindExceptionHandler(BindException bindException) {
        List<FieldError> fieldErrors = bindException.getFieldErrors();
        String msg = null;
        for (FieldError fieldError : fieldErrors) {
            msg = fieldError.getDefaultMessage();
        }
        return new Exception(msg);
    }

    //请求异常
    @ExceptionHandler(value = ConnectException.class)
    public Exception connectException(ConnectException e) {
        print(e);
        return new Exception("服务器异常,请联系管理员");
    }

    //IO流异常
    @ExceptionHandler(value = IOException.class)
    public Exception ioException(IOException e) {
        e.printStackTrace();
        return new Exception("IO流处理异常,请联系管理员");
    }

}

@ExceptionHandler注解使用方法

基本使用方法

Spring的@ExceptionHandler可以用来统一处理方法抛出的异常

比如这样:

@ExceptionHandler()
public String handleExeption2(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:默认";
    return resultStr;
}

当我们使用这个@ExceptionHandler注解时,我们需要定义一个异常的处理方法,比如上面的handlerException2()方法,给这个方法加上@ExceptionHandler注解,这个方法就好处理被@RequestMapping注解方法抛出的异常。

注解的参数

@ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常

比如:

@ExceptionHandler(NumberFormatException.class)
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}

此时注解的参数是NumberFormatException.class,表示只有方法抛出NumberFormatException时,才会调用该方法。

异常类型就近原则

当异常发生时,Spring会选择最接近抛出异常类型的处理方法。

比如之前提到的NumberFormatException,这个异常有父类RuntimeException,RuntimeException还有父类Exception,如果我们分别定义异常处理方法,@ExceptionHandler分别使用这三个异常作为参数

比如:

@ExceptionHandler(NumberFormatException.class)
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}
 
@ExceptionHandler()
public String handleExeption2(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:默认";
    return resultStr;
}
 
@ExceptionHandler(RuntimeException.class)
public String handleExeption3(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:RuntimeException";
    return resultStr;
}

那么,当代码抛出NumberFormatException时,调用的方法将是注解参数NumberFormatException.class的方法,也就是handleExeption(),而当代码抛出IndexOutOfBoundsException时,调用的方法将是注解参数RuntimeException的方法,也就是handleExeption3()。

注解方法的返回值

标识了@ExceptionHandler注解的方法,返回值类型和标识了@RequestMapping的方法是统一的,可参见@RequestMapping的说明,比如默认返回Spring的ModelAndView对象,也可以返回String,这时的String是ModelAndView的路径,而不是字符串本身。

有些情况下我们会给标识了@RequestMapping的方法添加@ResponseBody,比如使用Ajax的场景,直接返回字符串,异常处理类也可以如此操作,添加@ResponseBody注解后,可以直接返回字符串

比如这样:

@ExceptionHandler(NumberFormatException.class)
@ResponseBody
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}

这样的操作可以在执行完方法后直接返回字符串本身。

注解使用错误举例

使用@ExceptionHandler时尽量不要使用相同的注解参数。

如果我们定义两个处理相同异常的处理方法:

@ExceptionHandler(NumberFormatException.class)
@ResponseBody
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}
 
@ExceptionHandler(NumberFormatException.class)
@ResponseBody
public String handleExeption2(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:默认";
    return resultStr;
}

两个方法都处理NumberFormatException,这种定义方式编译可以通过,而当NumberFormatException真正被抛出时

Spring会给我们报错:

java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class java.lang.NumberFormatException]: {public java.lang.String TestController.handleExeption(java.lang.Exception), public java.lang.String TestController.handleExeption2(java.lang.Exception)}
    at org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.addExceptionMapping(ExceptionHandlerMethodResolver.java:102) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
...

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 如何在SpringBoot项目里进行统一异常处理

    目录 1.处理前 2.进行系统异常全局处理 3.进行自定义异常处理 效果 前言: 需要了解的知识: @ControllerAdvice的作用 1.处理前 异常代码: /** * 根据id获取医院设置 * * @param id 查看的id编号 * @return */ @ApiOperation(value = "根据id获取医院设置") @GetMapping("/findHospById/{id}") public Result findHospById(@Pa

  • SpringBoot中如何统一接口返回与全局异常处理详解

    目录 背景 统一接口返回 定义API返回码枚举类 定义正常响应的API统一返回体 定义异常响应的API统一返回体 编写包装返回结果的自定义注解 定义返回结果拦截器 WebMvc配置类拦截器注册者添加返回结果拦截器 编写响应体处理器 接口调用 测试结果 全局异常处理 编写自定义异常基类 编写自定义业务异常类 定义全局异常处理类 接口调用 测试结果 总结 背景 在分布式.微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式.前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,

  • Spring Boot统一处理全局异常的实战教程

    目录 注解的介绍 @ControllerAdvice @ExceptionHandler拦截异常并统一处理 代码实现 自定义异常 统一异常处理 前端返回值类 测试用例 附:Spring Boot默认的异常处理机制 总结 注解的介绍 @ControllerAdvice @ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理. 这里ControllerAdvice也可以这么理解,其抽象级别应该是

  • Springboot项目异常处理及返回结果统一

    目录 背景 返回结果定义 异常的定义 异常的处理 返回结果的处理 完整代码 使用示例 背景 在创建项目的初期,我们需要规范后端返回的数据结构,以便更好地与前端开发人员合作. 比如后端返回的数据为: {  "msg": "请跳转登陆页面", } 此时前端无法确定后端服务的处理结果是成功的还是失败的.在前端展示页面,成功与失败的展示是要作区分的,甚至不同的成功或失败结果要做出不同的展现效果,这也就是我们为什么要对返回结果做出统一规范的原因. 返回结果定义 public

  • Spring Boot全局统一异常处理器

    一.封装统一返回结果类 import com.jiusen.exercise.enums.ErrorEnum; import com.jiusen.exercise.exception.BusinessException; import lombok.Getter; import lombok.Setter; /** * @author: Lawson * @date: 2021/5/11 * @description: TODO 统一的返回结果 */ @Getter @Setter publi

  • SpringBoot中如何进行统一异常处理

    目录 1.处理前 2.进行系统异常全局处理 3.进行自定义异常处理 总结 如何在SpringBoot项目里进行统一异常处理 需要了解的知识 @ControllerAdvice的作用 1.处理前 异常代码 /** * 根据id获取医院设置 * * @param id 查看的id编号 * @return */ @ApiOperation(value = "根据id获取医院设置") @GetMapping("/findHospById/{id}") public Resu

  • Spring异常实现统一处理的方法

    目录 1.统一处理异常的机制 2.关于统一处理异常的方法 3.关于处理异常的方法的执行特点 1.统一处理异常的机制 Spring MVC框架提供了统一处理异常的机制!表现为每种类型的异常只需要写一段(写一次)处理此异常的代码即可,项目中无论处理哪种请求时,只要出现这种异常,都会自动执行这段代码! 提示:当项目中添加了spring-boot-starter-web依赖项,此依赖项中的核心就是Spring MVC框架的依赖项. 在实现统一处理异常之前,需要先使得控制器中处理请求的方法不再处理异常!则

  • SpringBoot接口如何统一异常处理

    目录 为什么要优雅的处理异常 实现案例 @ControllerAdvice异常统一处理 Controller接口 运行测试 进一步理解 @ControllerAdvice还可以怎么用? @ControllerAdvice是如何起作用的(原理)? 为什么要优雅的处理异常 如果我们不统一的处理异常,经常会在controller层有大量的异常处理的代码, 比如: @Slf4j @Api(value = "User Interfaces", tags = "User Interfac

  • 关于Spring统一异常处理及说明

    目录 相关注解概述 全局异常依赖包 示例 @ExceptionHandler注解使用方法 基本使用方法 注解的参数 异常类型就近原则 注解方法的返回值 注解使用错误举例 相关注解概述 通过使用@RestControllerAdvice或@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义: @RestControllerAdvice:返回Json格式数据: @ControllerAdvice:返回视图类型数据: @ResponseBody:和Cont

  • Spring Boot统一异常处理详解

    Spring Boot中默认带了error的映射,但是这个错误页面显示给用户并不是很友好. 统一异常处理 通过使用@ControllerAdvice定义统一异常处理的类,而不是在每个Controller中逐个定义. @ExceptionHandler用来定义函数针对的函数类型,最后将Exception对象和请求URL映射到URL中. @ControllerAdvice class ExceptionTranslator { public static final String DEFAULT_E

  • 详解使用Spring MVC统一异常处理实战

    1 描述 在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大. 那么,能不能将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护?答案是肯定的.下面将介绍使用Spring MVC统一处理异常的解决和实现过程. 2 分析 Spring MVC处理异常有3种方

  • Spring Cloud学习教程之Zuul统一异常处理与回退

    前言 Zuul 是Netflix 提供的一个开源组件,致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架.也有很多公司使用它来作为网关的重要组成部分,碰巧今年公司的架构组决定自研一个网关产品,集动态路由,动态权限,限流配额等功能为一体,为其他部门的项目提供统一的外网调用管理,最终形成产品(这方面阿里其实已经有成熟的网关产品了,但是不太适用于个性化的配置,也没有集成权限和限流降级). 本文主要给大家介绍了关于Spring Cloud Zuul统一异常处理与回退的相关内容,分享出来供大家

  • 详解Spring MVC/Boot 统一异常处理最佳实践

    前言 在 Web 开发中, 我们经常会需要处理各种异常, 这是一件棘手的事情, 对于很多人来说, 可能对异常处理有以下几个问题: 什么时候需要捕获(try-catch)异常, 什么时候需要抛出(throws)异常到上层. 在 dao 层捕获还是在 service 捕获, 还是在 controller 层捕获. 抛出异常后要怎么处理. 怎么返回给页面错误信息. 异常处理反例 既然谈到异常, 我们先来说一下异常处理的反例, 也是很多人容易犯的错误, 这里我们同时讲到前端处理和后端处理 : 捕获异常后

  • Spring中统一异常处理示例详解

    前言 系统很多地方都会抛出异常, 而Java的异常体系目标就是与逻辑解耦,Spring提供了统一的异常处理注解,用户只需要在错误的时候提示信息即可 在具体的SSM项目开发中,由于Controller层为处于请求处理的最顶层,再往上就是框架代码的. 因此,肯定需要在Controller捕获所有异常,并且做适当处理,返回给前端一个友好的错误码. 不过,Controller一多,我们发现每个Controller里都有大量重复的.冗余的异常处理代码,很是啰嗦. 能否将这些重复的部分抽取出来,这样保证Co

  • Spring Cloud zuul自定义统一异常处理实现方法

    Zuul在springcloud微服务体系中提供filer和router功能,是微服务不可或缺的部分.filer处理默认实现的外还可以自定义进行授权.限流.安全校验等,router完全可以替代Nginx反向代理.Zuul异常处理就是由SendErrorFilter完成. 在我们应用过程我们发现使用默认的异常filter有两个问题不是很友好: 1.无法快速识别出是否是请求路由的服务超时还是没有任何可用节点,发生错误只能查看日志通过堆栈去定位: 2.无法兼容自定义的譬如{code:500,msg:"

  • Spring Boot统一异常处理最佳实践(拓展篇)

    前言 之前一篇文章介绍了基本的统一异常处理思路: Spring MVC/Boot 统一异常处理最佳实践. 上篇文章也有许多人提出了一些问题: 如何区分 Ajax 请求和普通页面请求, 以分别返回 JSON 错误信息和错误页面. 如何结合 HTTP 状态码进行统一异常处理. 今天这篇文章就主要来讲讲这些, 以及其他的一些拓展点. 区分请求方式 其实 Spring Boot 本身是内置了一个异常处理机制的, 会判断请求头的参数来区分要返回 JSON 数据还是错误页面. 源码为: org.spring

  • Jersey框架的统一异常处理机制分析

    一.背景 写这边文章源于有朋友问过java中的checked exception和unchecked exception有啥区别,当时我对其的回答是:我编程时仅用RuntimeException.其实,我说句话我是有前提的,确切的应该这么说:在成熟的开发框架下编写业务代码,我只使用或关注RuntimeException.因为,由于框架往往将异常的处理统一封装,这样以便程序员更好的关注业务代码,而业务的一些错误通常是在系统运行期间发生的,因此业务的异常通常被设计为RuntimeException的

  • 详解Spring全局异常处理的三种方式

    在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大. 那么,能不能将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护?答案是肯定的.下面将介绍使用Spring MVC统一处理异常的解决和实现过程 使用Spring MVC提供的SimpleMappingE

随机推荐