Spring框架中如何实现并详细说明统一异常处理机制?

2026-05-25 21:161阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1631个文字,预计阅读时间需要7分钟。

Spring框架中如何实现并详细说明统一异常处理机制?

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

目录
  • 相关注解概述
  • 全局异常依赖包
  • 示例
  • @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注解方法抛出的异常。

Spring框架中如何实现并详细说明统一异常处理机制?

注解的参数

@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]
...

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。

本文共计1631个文字,预计阅读时间需要7分钟。

Spring框架中如何实现并详细说明统一异常处理机制?

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

目录
  • 相关注解概述
  • 全局异常依赖包
  • 示例
  • @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注解方法抛出的异常。

Spring框架中如何实现并详细说明统一异常处理机制?

注解的参数

@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]
...

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。