当前位置: 代码迷 >> 综合 >> springboot全局处理异常(@ControllerAdvice + @ExceptionHandler)
  详细解决方案

springboot全局处理异常(@ControllerAdvice + @ExceptionHandler)

热度:14   发布时间:2023-11-24 08:23:05.0

1.@ControllerAdvice

1.场景一

在构建RestFul的今天,我们一般会限定好返回数据的格式比如:

{"code": 0,"data": {},"msg": "操作成功"}

  但有时却往往会产生一些bug。这时候就破坏了返回数据的一致性,导致调用者无法解析。所以我们常常会定义一个全局的异常拦截器。

2.场景二

  对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚。

如此一来,我们的 Controller 层就不得不进行 try-catch Service 层的异常,否则会返回一些不友好的错误信息到客户端。但是,Controller 层每个方法体都写一些模板化的 try-catch 的代码,很难看也难维护,特别是还需要对 Service 层的不同异常进行不同处理的时候。

  @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理,只要设计得当,就再也不用在 Controller 层进行 try-catch 了!而且,@Validated 校验器注解的异常,也可以一起处理,无需手动判断绑定校验结果 BindingResult/Errors 了

3.@ControllerAdvice的使用

1.注意

  • 优点:将 Controller 层的异常和数据校验的异常进行统一处理,减少模板代码,减少编码量,提升扩展性和可维护性。
  • 缺点:只能处理 Controller 层未捕获(往外抛)的异常,对于 Interceptor(拦截器)层的异常,Spring 框架层的异常,就无能为力了。

2.@ControllerAdvice

  该注解是springMVC的注解

  • @ControllerAdvice是一个@Component,用于定义@ExceptionHandler@InitBinder@ModelAttribute方法,适用于所有使用@RequestMapping方法。

  • Spring4之前,@ControllerAdvice在同一调度的Servlet中协助所有控制器。Spring4已经改变:@ControllerAdvice支持配置控制器的子集,而默认的行为仍然可以利用。

  • 在Spring4中, @ControllerAdvice通过annotations()basePackageClasses()basePackages()方法定制用于选择控制器子集。

  不过据经验之谈,只有配合@ExceptionHandler最有用,其它两个不常用。

3.使用

  @ControllerAdvice 注解定义全局异常处理类

@ControllerAdvice
public class GlobalExceptionHandler {
}

  @ExceptionHandler 注解声明异常处理方法

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)@ResponseBodyString handleException(){return "Exception Deal!";}
}

  方法 handleException() 就会处理所有 Controller 层抛出的 Exception 及其子类的异常,这是最基本的用法了。

被 @ExceptionHandler 注解的方法的参数列表里,还可以声明很多种类型的参数,详见文档。其原型如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {/*** Exceptions handled by the annotated method. If empty, will default to any* exceptions listed in the method argument list.*/Class<? extends Throwable>[] value() default {};}

如果 @ExceptionHandler 注解中未声明要处理的异常类型,则默认为参数列表中的异常类型。所以上面的写法,还可以写成这样:

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler()@ResponseBodyString handleException(Exception e){return "Exception Deal! " + e.getMessage();}
}

参数对象就是 Controller 层抛出的异常对象!

示例

package com.cmc.schedule.handler;  import com.gionee.base.entity.JsonResult;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.beans.ConversionNotSupportedException;  
import org.springframework.beans.TypeMismatchException;  
import org.springframework.http.converter.HttpMessageNotReadableException;  
import org.springframework.http.converter.HttpMessageNotWritableException;  
import org.springframework.web.HttpMediaTypeNotAcceptableException;  
import org.springframework.web.HttpRequestMethodNotSupportedException;  
import org.springframework.web.bind.MissingServletRequestParameterException;  
import org.springframework.web.bind.annotation.ControllerAdvice;  
import org.springframework.web.bind.annotation.ExceptionHandler;  
import org.springframework.web.bind.annotation.ResponseBody;  import java.io.IOException;  /**  * 异常拦截处理器  *  * @author chenmc  */  
@ControllerAdvice  
@ResponseBody  
public class GlobalExceptionHandler {  private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";  private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);  //运行时异常    @ExceptionHandler(RuntimeException.class)  public String runtimeExceptionHandler(RuntimeException ex) {  return resultFormat(1, ex);  }  //空指针异常    @ExceptionHandler(NullPointerException.class)  public String nullPointerExceptionHandler(NullPointerException ex) {  return resultFormat(2, ex);  }  //类型转换异常    @ExceptionHandler(ClassCastException.class)  public String classCastExceptionHandler(ClassCastException ex) {  return resultFormat(3, ex);  }  //IO异常    @ExceptionHandler(IOException.class)  public String iOExceptionHandler(IOException ex) {  return resultFormat(4, ex);  }  //未知方法异常    @ExceptionHandler(NoSuchMethodException.class)  public String noSuchMethodExceptionHandler(NoSuchMethodException ex) {  return resultFormat(5, ex);  }  //数组越界异常    @ExceptionHandler(IndexOutOfBoundsException.class)  public String indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {  return resultFormat(6, ex);  }  //400错误    @ExceptionHandler({HttpMessageNotReadableException.class})  public String requestNotReadable(HttpMessageNotReadableException ex) {  System.out.println("400..requestNotReadable");  return resultFormat(7, ex);  }  //400错误    @ExceptionHandler({TypeMismatchException.class})  public String requestTypeMismatch(TypeMismatchException ex) {  System.out.println("400..TypeMismatchException");  return resultFormat(8, ex);  }  //400错误    @ExceptionHandler({MissingServletRequestParameterException.class})  public String requestMissingServletRequest(MissingServletRequestParameterException ex) {  System.out.println("400..MissingServletRequest");  return resultFormat(9, ex);  }  //405错误    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})  public String request405(HttpRequestMethodNotSupportedException ex) {  return resultFormat(10, ex);  }  //406错误    @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})  public String request406(HttpMediaTypeNotAcceptableException ex) {  System.out.println("406...");  return resultFormat(11, ex);  }  //500错误    @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})  public String server500(RuntimeException ex) {  System.out.println("500...");  return resultFormat(12, ex);  }  //栈溢出  @ExceptionHandler({StackOverflowError.class})  public String requestStackOverflow(StackOverflowError ex) {  return resultFormat(13, ex);  }  //其他错误  @ExceptionHandler({Exception.class})  public String exception(Exception ex) {  return resultFormat(14, ex);  }  private <T extends Throwable> String resultFormat(Integer code, T ex) {  ex.printStackTrace();  log.error(String.format(logExceptionFormat, code, ex.getMessage()));  return JsonResult.failed(code, ex.getMessage());  }  } 

注:  

  springboot中这样就可以了!!!

  如果是springMVC配置文件中增加下面的配置

<!-- 处理异常 -->  <context:component-scan base-package="com.gionee.xo" use-default-filters="false">    <!-- base-package 如果多个,用“,”分隔 -->    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />    <!--控制器增强,使一个Contoller成为全局的异常处理类,类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常-->    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />    </context:component-scan>