Spring Boot全局异常处理器

Spring Boot全局异常处理器

1.什么是全局异常处理器

​ 软件开发springboot项目过程中,不可避免的需要处理各种异常,spring mvc架构中各层会出现大量的try{…} catch{…} finally{…}代码块,不仅有大量的冗余代码,而且还影响代码的可读性。这样就需要定义个全局统一异常处理器,以便业务层再也不必处理异常。

  • ​ 我们使用参数校验器的时候,参数校验不通过会抛异常,无法使用try-catch捕获,只能使用全局异常处理器

  • 自定义异常,只能用全局异常来捕获。不能直接返回给客户端,客户端是看不懂的,需要接入全局异常处理器

2.原理和方法

​ 异常阶段分类可以分为:进入Controller前的异常和Service层的异常。参数校验就属于进入Controller前的异常。
Spring Boot全局异常处理器_第1张图片

​ 方法就是使用SpringBoot提供的@ControllerAdvice注解,把异常处理器应用到所有控制器,而不是单个控制器。借助该注解,我们可以实现:在独立的某个地方,比如单独的一个类,定义一套对各章异常的处理机制,然后在类的签名加上注解@ControllerAdvice,统一对不同阶段的,不同异常进行处理。这就是统一异常处理的原理。

3.@ControllerAdvice注解

​ @ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理,对于@ControllerAdvice,我们比较熟悉的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解。

4.使用方式

1.创建handler包,用于存放项目的全局异常处理信息

2.创建好全局处理类,并在类上使用@RestControllerAdvice注解,也可以使用@ControllerAdvice

3.创建好类后就写一个方法去实现参数校验失败的逻辑

@RestControllerAdvice
public class ValidatedException {

    @ExceptionHandler
    public R UnValidateException(MethodArgumentNotValidException e){
        BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        StringBuilder sb = new StringBuilder();
        for (FieldError fieldError : fieldErrors) {
            sb.append(fieldError.getDefaultMessage()).append(" ");
        }
        return R.Failed(sb.toString());
    }

}

使用@ControllerAdvice注解的话我们需要在方法上面加上@ReponseBody注解

  • MethodArgumentNotValidException 是 Spring 框架中的一个异常类型,表示方法参数验证失败。当使用 Spring MVC 或 Spring Boot 等框架处理 Web 请求时,你可以在方法参数上使用 @Valid 注解来启用数据验证功能,并使用注解来定义验证规则。当请求参数不符合规则时,框架将抛出 MethodArgumentNotValidException 异常。

除了用于参数校验的`MethodArgumentNotValidException类,我们还可以自定义基础异常类

	@ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("异常信息:{}", ex.getMessage());
        return Result.Failed(ex.getMessage());
    }
public class BaseException extends RuntimeException {

    public BaseException() {
    }

    public BaseException(String msg) {
        super(msg);
    }

}

定义完后我们就可以使用自己定义这个异常类来抛出异常

public void someMethod() {
    // 发生异常情况
    if (someCondition) {
        throw new BaseException("发生了 BaseException 异常");
    }
}

这样就可以将异常信息返回给前端,而不是以报错的提示返回。

5.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的

用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的。

  • 由@InitBinder表示的方法,可以对WebDataBinder对象进行初始化。WebDataBinder是DataBinder的子类,用于完成由表单到JavaBean属性的绑定。
  • 在使用SpringMVC的时候,经常会遇到表单中的日期字符串和JavaBean的Date类型,而SpringMVC默认不支持这个格式的转换,所以需要手动配置,自定义数据的绑定才能解决这个问题。在需要日期转换的Controller中使用SpringMVC的注解@Initbinder和Spring自带的WebDateBinder类来操作。WebDataBinder是用来绑定请求参数到指定的属性编辑器。由于前端传到controller里的值是String类型的,当往Model里Set这个值的时候,如果set的这个属性是个对象,Spring就会去找对应的editor进行转换,然后再set进去。
  • 标注在controller上的@InitBinder只对当前Controller生效,要想全局生效,可以使用@ControllerAdivce。通过@ControllerAdvice可以将对于控制器的全局配置放置在同一个位置。
@ControllerAdvice
public class MyControllerAdvice {
 
    /**
     * 转换前端传入的日期变量参数为指定格式。
     *
     * @param binder 数据绑定参数。
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class,
                new CustomDateEditor(new SimpleDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT), false));
    }
 }

6.@ControllerAdvice注解作用原理

spring在实现时,首先前端控制器DispatcherServlet对象在创建时会初始化一系列对象:

public class DispatcherServlet extends FrameworkServlet{
  //......
  protected void initStrategies(ApplicationContext context){
      initMultipartResolver(context);
      initLocaleResolver(context);
      initThemeResolver(context);
      initHandlerMappings(context);
      initHandlerAdapters(context);
      initHandlerExceptionResolvers(context);
      initRequestToViewNameTranslator(context);
      initViewResolvers(context);
      initFlashMapManager(context);        
  }    
  //......  
}

​ 对于@ControllerAdvice注解,我们重点关注initHandlerAdapters(context)和initHandlerExceptionResolvers(context)这两个方法。

  • initHandlerAdapters(context)方法会取得所有实现了HandlerAdapter接口的bean并保存起来,其中就有一个类型为RequestMappingHandlerAdapter的bean,这个bean就是@RequestMapping注解能起作用的关键,这个bean在应用启动过程中会获取所有被@ControllerAdvice注解标注的bean对象做进一步处理:找到所有ModelAttribute标注的方法并缓存起来,找到所有InitBinder标注的方法并缓存起来。经过处理之后,@ModelAttribute和@InitBinder就能起作用了。
  • DispatcherServlet的initHandlerExceptionResolvers(context)方法,方法会取得所有实现了HandlerExceptionResolver接口的bean并保存起来,七张就有一个类型为ExceptionHandlerExceptionResolver的bean,这个bean在应用启动过程中会获取所有被@ControllerAdvice注解标注的bean对象做进一步处理。

7.@RestControllerAdvice注解

​ 按住Ctrl鼠标点击RestController注解,可以看到它其实一个组合注解,是@ResponseBody和@ControllerAdvice的组合

Spring Boot全局异常处理器_第2张图片

@ResponseBody:

  • @responsebody这个注解表示你的返回值将存在responsebody中返回到前端,也就是将return返回值作为请求返回值,return的数据不会解析成返回跳转路径,将java对象转为json格式的数据,前端接收后会显示将数据到页面,如果不加的话 返回值将会作为url的一部分,页面会跳转到这个url,也就是跳转到你返回的这个路径。

  • @ResponseBody这个注解通常使用在控制层(controller)的方法上,其作用是将方法的返回值以特定的格式写入到response的body区域,进而将数据返回给客户端。当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。

  • @ResponseBody这个注解使用情景:当返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用,常用在ajax异步请求中,可以通过 ajax 的“success”:fucntion(data){} data直接获取到。

  • @ResponseBody这个注解一般是作用在方法上的,加上该注解表示该方法的返回结果直接写到Http response Body中,在RequestMapping中 return返回值默认解析为跳转路径,如果你此时想让Controller返回一个字符串或者对象到前台。
    (data){} data直接获取到。

  • @ResponseBody这个注解一般是作用在方法上的,加上该注解表示该方法的返回结果直接写到Http response Body中,在RequestMapping中 return返回值默认解析为跳转路径,如果你此时想让Controller返回一个字符串或者对象到前台。

你可能感兴趣的:(spring,boot,后端,java)