spring中通用异常的处理

代码
编写实体类:

 @Data
    public class Item {
        private Integer id;
        private String name;
        private Long price;
    }

编写业务:

service:
@Service
public class ItemService {
    
    public Item saveItem(Item item){
        int id = new Random().nextInt(100);
        item.setId(id);
        return item;
    }
}
  • 这里临时使用随机生成id,然后直接返回,没有做数据库操作

controller:

@RestController
public class ItemController {

    @Autowired
    private ItemService itemService;

    @PostMapping("item")
    public ResponseEntity<Item> saveItem(Item item){
        // 如果价格为空,则抛出异常,返回400状态码,请求参数有误
        if(item.getPrice() == null){
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }
        Item result = itemService.saveItem(item);
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }
}

统一异常处理

最基础的形态

@ControllerAdvice
@Slf4j
public class BasicExceptionAdvice {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<String> handleException(RuntimeException e) {
        // 我们暂定返回状态码为400, 然后从异常中获取友好提示信息
        return ResponseEntity.status(400).body(e.getMessage());
    }
}

解读:

  • @ControllerAdvice:默认情况下,会拦截所有加了@Controller的类

  • @ExceptionHandler(RuntimeException.class):作用在方法上,声明要处理的异常类型,可以有多个,这里指定的是RuntimeException。被声明的方法可以看做是一个SpringMVC的Handler:

    • 参数是要处理的异常,类型必须要匹配
    • 返回结果可以是ModelAndView、ResponseEntity等,基本与handler类似
  • 这里等于从新定义了返回结果,我们可以随意指定想要的返回类型。此处使用了String

要想在商品服务中扫描到这个advice,需要在service中引入lcommon依赖:

   <dependency>
        <groupId>com.zyf.common</groupId>
        <artifactId>common</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </dependency>

自定义异常

刚才的处理看似完美,但是仔细想想,我们在通用的异常处理中,把返回状态码写死为400了:

这样显然不太合理。

但是,仅凭用户抛出的异常,我们根本无法判断到底该返回怎样的状态码,可能是参数有误、数据库异常、没有权限,等等各种情况。

因此,用户抛出异常时,就必须传递两个内容:

  • 异常信息
  • 异常状态码

但是RuntimeException是无法接受状态码的,只能接受异常的消息,所以我们需要做两件事情:

  • 自定义异常,来接受状态码、异常消息
  • 状态码与异常消息可能会重复使用,我们通过枚举来把这些信息变为常量

我们定义一个枚举,用于封装异常状态码和异常信息:

@Getter
public enum ExceptionEnum {
    PRICE_CANNOT_BE_NULL(400, "价格不能为空!");
    ;
    private int status;
    private String message;

    ExceptionEnum(int status, String message) {
        this.status = status;
        this.message = message;
    }
}

然后自定义异常,来获取枚举对象。

自定义异常类:

代码:

@Getter
public class LyException extends RuntimeException {
    private int status;

    public LyException(ExceptionEnum em) {
        super(em.getMessage());
        this.status = em.getStatus();
    }

    public LyException(ExceptionEnum em, Throwable cause) {
        super(em.getMessage(), cause);
        this.status = em.getStatus();
    }
}
  • status:响应状态码

修改controller代码:

@PostMapping("item")
public ResponseEntity<Item> saveItem(Item item){
    if(item.getPrice() == null){
        throw new LyException(ExceptionEnum.PRICE_CANNOT_BE_NULL);
    }
    Item result = itemService.saveItem(item);
    return ResponseEntity.ok(result);
}

在刚刚编写的BasicExceptionAdvice中,我们返回的结果是String,为了让异常结果更友好,我们返回定义异常结果对象:

 @Getter
    public class ExceptionResult {
        private int status;
        private String message;
        private String timestamp;
    
        public ExceptionResult(LyException e) {
            this.status = e.getStatus();
            this.message = e.getMessage();
            this.timestamp = DateTime.now().toString("yyyy-MM-dd HH:mm:ss");
        }
    }

这里使用了日期工具类:JodaTime,我们要引入依赖在ly-common中:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
</dependency>

修改异常处理逻辑:

 @ControllerAdvice
    @Slf4j
    public class BasicExceptionAdvice {
    
        @ExceptionHandler(LyException.class)
        public ResponseEntity<ExceptionResult> handleLyException(LyException e) {
            return ResponseEntity.status(e.getStatus()).body(new ExceptionResult(e));
        }
    
        @ExceptionHandler(RuntimeException.class)
        public ResponseEntity<String> handleRuntimeException(RuntimeException e) {
            return ResponseEntity.status(500).body(e.getMessage());
        }
    }

再次测试:

以后,我们无论controller还是service层的业务处理,出现异常情况,都抛出自定义异常,方便统一处理。

你可能感兴趣的:(java)