11-SpringBoot 工程中的异常处理方式

背景分析

在项目的开发中,不管是对底层的数据逻辑操作过程,还是业务逻辑的处理过程,还是控制逻辑的处理过程,都不可避免会遇到各种可预知的、不可预知的异常。处理好异常对系统有很好的保护作用,同时会大大提高用户的体验。

异常处理分析

概述

Java项目中处理异常方式无非两种,要么执行trycatch操作,要么执行throw操作(抛给其它对象处理),无论采用哪种方式,其目的是让我们的系统对异常要有反馈。但现在的问题是我们如何让这种反馈代码的编写即简单又直观、友好。

处理规范

我们在处理异常的过程中通常要遵循一定的设计规范,例如:

  • 捕获异常时与抛出的异常必须完全匹配,或者捕获异常是抛出异常的父类类型。
  • 避免直接抛出RuntimeException,更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常(例如ServiceException)。
  • 捕获异常后必须进行处理(例如记录日志)。如果不想处理它,需要将异常抛给它的调用者。
  • 最外层的逻辑必须处理异常,将其转化成用户可以理解的内容。
  • 避免出现重复的代码(Don’t Repeat Yourself),即DAY原则。

SpringBoot 工程下的异常处理

准备工作

第一步:创建项目或module,并添加web依赖,代码如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

第二步:修改项目访问端口为80,例如

server.port=80

第三步:定义Controller类,代码如下:

package com.cy.pj.arithmetic.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class ArithmeticController {

  @RequestMapping("doCompute/{n1}/{n2}")
  @ResponseBody
  public String doCompute(@PathVariable  Integer n1, 
  @PathVariable Integer n2){
          Integer result=n1/n2;
          return "Result is "+result;
  }
}

第四步启动项目进行访问测试

在浏览器地址栏输入http://localhost/doCompute/10/2,检测输出结果。

Result is 5

默认异常处理

在浏览器地址栏输入http://localhost/doCompute/10/0,检测输出结果。

image.png

对于这样的默认异常处理(spring boot提供),用户体验不太友好,为了呈现更加友好的异常信息,我们通常要对异常进行自定义处理。

自己try异常处理

在控制层方法中,我们可以进行try catch处理,例如:

  @RequestMapping("doCompute/{n1}/{n2}")
  @ResponseBody
  public String doCompute(@PathVariable  Integer n1, 
  @PathVariable Integer n2){
          try{
          Integer result=n1/n2;
          return "Result is "+result;
          }catch(ArithmeticException e){
          return "exception is "+e.getMessage();
          }
  }
  

一个Controller类中通常会有多个方法,这样多个方法中都写try语句进行异常处理会带来大量重复代码的编写,不易维护。

Controller内部定义异常处理方法

在Controller类中添加异常处理方法,代码如下:

@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public String doHandleArithmeticException(ArithmeticException e){
    e.printStackTrace();
    return "计算过程中出现了异常,异常信息为"+e.getMessage();
}

@ExceptionHandler注解描述的方法为异常处理方法(注解中的异常类型为可处理的异常类型),假如Controller类中的逻辑方法中出现异常后没有处理异常,则会查找Controller类中有没有定义异常处理方法,假如定义了,且可以处理抛出的异常类型,则由异常处理方法处理异常。

控制层中的全局异常处理类及方法定义

当项目由多个控制层类中有多个共性异常的处理方法定义时,我们可以将这些方法提取到公共的父类对象中,但是这种方式是一种强耦合的实现,不利于代码的维护。我们还可以借助spring框架中web模块定义的全局异常处理规范进行实现,例如定义全局异常处理类,代码如下:

package com.cy.pj.common.web;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ArithmeticException.class)
    public String doHandleArithmeticException(ArithmeticException e){
        e.printStackTrace();
        return  "计算过程中出现了异常,异常信息为"+e.getMessage();
    }
}

其中,@RestControllerAdvice 注解描述的类为全局异常处理类,当控制层方法中的异常没有自己捕获,也没有定义其内部的异常处理方法,底层默认会查找全局异常处理类,调用对应的异常处理方法进行异常处理。如图所示:

image.png

总结(Summary)

本小节主要是对springboot中的异常处理机制进行了简单分析和讲解。目的是掌握springboot工程下的异常处理方式,并基于业务的不同进行响应的异常处理。从而有效提高其用户体验,加强系统的容错能力。


springboot 最佳实践
SpringBoot技术的势、道、术分析与实践。

以终为始,闭环迭代,持续提高。

4.2k 声望
17.2k 粉丝
0 条评论
推荐阅读
常见JVM基础面试必备
为什么要学习JVM?你是如何理解JVM的?市场上有哪些主流的JVM呢?JVM的体系结构是怎样的?JVM 是如何工作的?JVM有哪些运行模式吗?JVM内存布局是怎样的?JVM垃圾回收算法有哪些?JVM中可以作为 Root 的对象?JVM...

Jason2阅读 1.5k

spring boot 锁
由于当前的项目中由于多线程操作同一个实体,会出现数据覆盖的问题,后保存的实体把先保存的实体的数据给覆盖了。于是查找了锁的实现的几种方式。但写到最后发现,其实自己可以写sql 更新需要更新的字段即可,这...

weiewiyi3阅读 9.1k

利用Docker部署管理LDAP及其初次使用
前言:本周主要写了gitlabWebhook转github的项目,总体上没有遇到什么大问题,这周接触到了LDAP,于是就花时间实际操作了解了一下。

李明5阅读 823

记录本周问题
项目里两个地方都用到了hashmap。但是感觉自己用的时候并没有感觉非常的清晰。同时发现hashmap有线程不安全问题,而自己用的时候就是多线程来使用。于是在这里介绍一下。

weiewiyi5阅读 666

记录java 在遍历中删除元素 以及 mysql5.6版本添加unique失败
遍历中删除List或Queue等数据结构中,如何一边遍历一遍删除?1. 常犯错误ArrayList可能没遇到坑过的人会用增强for循环这么写: {代码...} 但是一运行,结果却抛 java.util.ConcurrentModificationException 异常即...

weiewiyi4阅读 733

Spring Security + JWT
Spring Security默认是基于session进行用户认证的,用户通过登录请求完成认证之后,认证信息在服务器端保存在session中,之后的请求发送上来后SecurityContextPersistenceFilter过滤器从session中获取认证信息、...

4阅读 1.3k

Reactive Spring实战 -- 理解Reactor的设计与实现
Reactor是Spring提供的非阻塞式响应式编程框架,实现了Reactive Streams规范。 它提供了可组合的异步序列API,例如Flux(用于[N]个元素)和Mono(用于[0 | 1]个元素)。

binecy3阅读 4.7k评论 2

以终为始,闭环迭代,持续提高。

4.2k 声望
17.2k 粉丝
宣传栏