SpringBoot系列之统一异常处理

本章内容:

  • @ControllerAdvice的三种使用方法以及具体场景
  • 在SpringBoot中如何配置全局异常处理
  • 如何添加异常处理方法
  • 如何自定义异常

 

一、@ControllerAdvice的三种用法

@ControllerAdvice ,这是一个增强的 Controller。使用这个 Controller的注解 ,可以实现三个方面的功能:

  • 全局异常处理
  • 全局数据绑定
  • 全局数据预处理

 灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是 SpringMVC 提供的功能,在 Spring Boot 中可以直接使用,下面分别来看。

1.1 全局异常处理

使用 @ControllerAdvice 实现全局异常处理,只需要定义类,添加该注解即可定义方式如下:

@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ModelAndView customException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("message", e.getMessage());
        mv.setViewName("myerror");
        return mv;
    }
}

在该类中,可以定义多个方法,不同的方法处理不同的异常,例如专门处理空指针的方法、专门处理数组越界的方法…,也可以直接向上面代码一样,在一个方法中处理所有的异常信息。

@ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为 NullpointerException,则数组越界异常就不会进到这个方法中来。

1.2 全局数据绑定

全局数据绑定功能可以用来做一些初始化的数据操作,我们可以将一些公共的数据定义在添加了 @ControllerAdvice 注解的类中,这样,在每一个 Controller 的接口中,就都能够访问导致这些数据。

使用步骤,首先定义全局数据,如下:

@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ModelAttribute(name = "md")
    public Map<String,Object> mydata() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("age", 99);
        map.put("gender", "男");
        return map;
    }
}

使用 @ModelAttribute 注解标记该方法的返回数据是一个全局数据,默认情况下,这个全局数据的 key 就是返回的变量名,value 就是方法返回值,当然开发者可以通过 @ModelAttribute 注解的 name 属性去重新指定 key。

定义完成后,在任何一个Controller 的接口中,都可以获取到这里定义的数据:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        Map<String, Object> map = model.asMap();
        System.out.println(map);
        int i = 1 / 0;
        return "hello controller advice";
    }
}

测试效果

1.3 全局数据预处理

定义有两个实体类,Book 和 Author,分别定义如下:

public class Book {
    private String name;
    private Long price;
    //getter/setter
}
public class Author {
    private String name;
    private Integer age;
    //getter/setter
}

此时,如果我定义一个数据添加接口,如下:

@PostMapping("/book")
public void addBook(Book book, Author author) {
    System.out.println(book);
    System.out.println(author);
}

这个时候,添加操作就会有问题,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分。此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题

解决步骤如下:

1.给接口中的变量取别名

@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
    System.out.println(book);
    System.out.println(author);
}

2.进行请求数据预处理

在 @ControllerAdvice 标记的类中添加如下代码:

@InitBinder("b")
public void b(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void a(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("a.");
}

@InitBinder(“b”) 注解表示该方法用来处理和Book和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有b前缀

3.发送请求

请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分.

二、SpringBoot当中的全局异常处理

在项目开发过程中,不管是对底层数据库的操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。如果对每个过程都单独作异常处理,那系统的代码耦合度会变得很高,此外,开发工作量也会加大而且不好统一,这也增加了代码的维护成本。 针对这种实际情况,我们需要将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能单一,也实现了异常信息的统一处理和维护。同时,我们也不希望直接把异常抛给用户,应该对异常进行处理,对错误信息进行封装,然后返回一个友好的信息给用户。这节主要总结一下项目中如何使用 Spring Boot 如何拦截并处理全局的异常。

关键注解:

  • @ControllerAdvice 捕获 Controller 层抛出的异常,如果添加 @ResponseBody 返回信息则为JSON 格式
  • @RestControllerAdvice 相当于 @ControllerAdvice 与 @ResponseBody 的结合体
  • @ExceptionHandler 统一处理一种类的异常,减少代码重复率,降低复杂度

参考技术文档:https://www.ycbbs.vip/?p=1281 

2.1 创建全局统一异常处理器

第一步:创建统一异常处理器

package com.test.common.handler;

/**
 * 统一异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(Exception.class)
	@ResponseBody
	public Result error(Exception e){
		e.printStackTrace();
		return Result.error();
	}
}

第二步:扫描异常处理器

在SpringBoot启动类上添加了注解@ComponentScan

@ComponentScan(basePackages={"com.test.common"})

第三步:测试

2.2 添加异常处理方法

在GlobalExceptionHandler.java中添加异常(可以根据实际项目中出现的异常后进行异常处理方法添加,也可以提前将所有常用的异常提前罗列出来)

@ExceptionHandler(BadSqlGrammarException.class)
@ResponseBody
public Result error(BadSqlGrammarException e){
    e.printStackTrace();
    return Result.setResult(ResultCodeEnum.BAD_SQL_GRAMMAR);
}

在添加了统一日志处理后,可以将异常处理方法改为

@ExceptionHandler(BadSqlGrammarException.class)
@ResponseBody
public Result error(BadSqlGrammarException e){
   log.error(ExceptionUtil.getMessage(e));
    return Result.setResult(ResultCodeEnum.BAD_SQL_GRAMMAR);
}

这样修改后日志就会输出到相应的日志系统当中了

2.3 自定义异常

1)自定义异常

common模块中创建com.test.common.exception包,创建TestException.java通用异常类 继承 RuntimeException,其中,RuntimeException 对代码没有侵入性

package com.test.common.exception;

@Data
@ApiModel(value = "全局异常")
public class TestException extends RuntimeException {

	@ApiModelProperty(value = "状态码")
	private Integer code;

	/**
	 * 接受状态码和消息
	 * @param code
	 * @param message
	 */
	public TestException(Integer code, String message) {
		super(message);
		this.code=code;
	}
}

2)在特定位置跑出自定义异常

public Result pageQuery(......){

    if(page <= 0 || limit <= 0){
        //throw new TestException(21003, "参数不正确1");
        throw new TestException(ResultCodeEnum.PARAM_ERROR);
    }
    ......
}

3) 添加特定异常处理方法

@ExceptionHandler(TestException.class)
@ResponseBody
public Result error(TestException e){
    e.printStackTrace();
    return Result.error().message(e.getMessage()).code(e.getCode());
}

 

おすすめ

転載: blog.csdn.net/jatej/article/details/104353146