看看人家 SpringBoot 的全局异常处理,多么优雅…

沙海 2021年4月13日03:33:42杂谈 Java评论34字数 10649阅读35分29秒阅读模式
摘要

速读摘要

速读摘要文章源自JAVA秀-https://www.javaxiu.com/11758.html

Redis单线程已经很快了,为什么6.0要引入多线程?最近面试BATJ,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。文章源自JAVA秀-https://www.javaxiu.com/11758.html

原文约 3080 | 图片 6 | 建议阅读 7 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/11758.html

看看人家 SpringBoot 的全局异常处理,多么优雅...

xuwujing Java学习者社区 文章源自JAVA秀-https://www.javaxiu.com/11758.html

看看人家 SpringBoot 的全局异常处理,多么优雅…文章源自JAVA秀-https://www.javaxiu.com/11758.html

文章源自JAVA秀-https://www.javaxiu.com/11758.html

篇文章主要介绍的是SpringBoot项目进行全局异常的处理。文章源自JAVA秀-https://www.javaxiu.com/11758.html

SpringBoot全局异常准备

说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码。文章源自JAVA秀-https://www.javaxiu.com/11758.html

开发准备

环境要求JDK:1.8SpringBoot:1.5.17.RELEASE文章源自JAVA秀-https://www.javaxiu.com/11758.html

首先还是Maven的相关依赖:文章源自JAVA秀-https://www.javaxiu.com/11758.html

  <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <java.version>1.8</java.version>        <maven.compiler.source>1.8</maven.compiler.source>        <maven.compiler.target>1.8</maven.compiler.target>    </properties>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.5.17.RELEASE</version>        <relativePath />    </parent>    <dependencies>        <!-- Spring Boot Web 依赖 核心 -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!-- Spring Boot Test 依赖 -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.41</version>        </dependency>    </dependencies>

配置文件这块基本不需要更改,全局异常的处理只需在代码中实现即可。文章源自JAVA秀-https://www.javaxiu.com/11758.html

代码编写

SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。SpringBoot中有一个ControllerAdvice的注解,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用ExceptionHandler注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。文章源自JAVA秀-https://www.javaxiu.com/11758.html

我们根据下面的这个示例来看该注解是如何使用吧。文章源自JAVA秀-https://www.javaxiu.com/11758.html

示例代码:文章源自JAVA秀-https://www.javaxiu.com/11758.html

@ControllerAdvicepublic class MyExceptionHandler {    @ExceptionHandler(value =Exception.class) public String exceptionHandler(Exception e){  System.out.println("未知异常!原因是:"+e);        return e.getMessage();    }}

上述的示例中,我们对捕获的异常进行简单的二次处理,返回异常的信息,虽然这种能够让我们知道异常的原因,但是在很多的情况下来说,可能还是不够人性化,不符合我们的要求。那么我们这里可以通过自定义的异常类以及枚举类来实现我们想要的那种数据吧。文章源自JAVA秀-https://www.javaxiu.com/11758.html

自定义基础接口类文章源自JAVA秀-https://www.javaxiu.com/11758.html

首先定义一个基础的接口类,自定义的错误描述枚举类需实现该接口。代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

public interface BaseErrorInfoInterface {    /** 错误码*/  String getResultCode(); /** 错误描述*/  String getResultMsg();}

自定义枚举类文章源自JAVA秀-https://www.javaxiu.com/11758.html

然后我们这里在自定义一个枚举类,并实现该接口。代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

public enum CommonEnum implements BaseErrorInfoInterface { // 数据操作错误定义 SUCCESS("200", "成功!"),  BODY_NOT_MATCH("400","请求的数据格式不符!"), SIGNATURE_NOT_MATCH("401","请求的数字签名不匹配!"), NOT_FOUND("404", "未找到该资源!"),  INTERNAL_SERVER_ERROR("500", "服务器内部错误!"), SERVER_BUSY("503","服务器正忙,请稍后再试!") ; /** 错误码 */ private String resultCode; /** 错误描述 */ private String resultMsg; CommonEnum(String resultCode, String resultMsg) {  this.resultCode = resultCode;  this.resultMsg = resultMsg; } @Override public String getResultCode() {  return resultCode; } @Override public String getResultMsg() {  return resultMsg; }}

自定义异常类文章源自JAVA秀-https://www.javaxiu.com/11758.html

然后我们在来自定义一个异常类,用于处理我们发生的业务异常。代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

public class BizException extends RuntimeException { private static final long serialVersionUID = 1L; /**  * 错误码  */ protected String errorCode; /**  * 错误信息  */ protected String errorMsg; public BizException() {  super(); } public BizException(BaseErrorInfoInterface errorInfoInterface) {  super(errorInfoInterface.getResultCode());  this.errorCode = errorInfoInterface.getResultCode();  this.errorMsg = errorInfoInterface.getResultMsg(); } public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {  super(errorInfoInterface.getResultCode(), cause);  this.errorCode = errorInfoInterface.getResultCode();  this.errorMsg = errorInfoInterface.getResultMsg(); } public BizException(String errorMsg) {  super(errorMsg);  this.errorMsg = errorMsg; } public BizException(String errorCode, String errorMsg) {  super(errorCode);  this.errorCode = errorCode;  this.errorMsg = errorMsg; } public BizException(String errorCode, String errorMsg, Throwable cause) {  super(errorCode, cause);  this.errorCode = errorCode;  this.errorMsg = errorMsg; } public String getErrorCode() {  return errorCode; } public void setErrorCode(String errorCode) {  this.errorCode = errorCode; } public String getErrorMsg() {  return errorMsg; } public void setErrorMsg(String errorMsg) {  this.errorMsg = errorMsg; } public String getMessage() {  return errorMsg; } @Override public Throwable fillInStackTrace() {  return this; }}

自定义数据格式文章源自JAVA秀-https://www.javaxiu.com/11758.html

顺便这里我们定义一下数据的传输格式。代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

public class ResultBody { /**  * 响应代码  */ private String code; /**  * 响应消息  */ private String message; /**  * 响应结果  */ private Object result; public ResultBody() { } public ResultBody(BaseErrorInfoInterface errorInfo) {  this.code = errorInfo.getResultCode();  this.message = errorInfo.getResultMsg(); } public String getCode() {  return code; } public void setCode(String code) {  this.code = code; } public String getMessage() {  return message; } public void setMessage(String message) {  this.message = message; } public Object getResult() {  return result; } public void setResult(Object result) {  this.result = result; } /**  * 成功  *   * @return  */ public static ResultBody success() {  return success(null); } /**  * 成功  * @param data  * @return  */ public static ResultBody success(Object data) {  ResultBody rb = new ResultBody();  rb.setCode(CommonEnum.SUCCESS.getResultCode());  rb.setMessage(CommonEnum.SUCCESS.getResultMsg());  rb.setResult(data);  return rb; } /**  * 失败  */ public static ResultBody error(BaseErrorInfoInterface errorInfo) {  ResultBody rb = new ResultBody();  rb.setCode(errorInfo.getResultCode());  rb.setMessage(errorInfo.getResultMsg());  rb.setResult(null);  return rb; } /**  * 失败  */ public static ResultBody error(String code, String message) {  ResultBody rb = new ResultBody();  rb.setCode(code);  rb.setMessage(message);  rb.setResult(null);  return rb; } /**  * 失败  */ public static ResultBody error( String message) {  ResultBody rb = new ResultBody();  rb.setCode("-1");  rb.setMessage(message);  rb.setResult(null);  return rb; } @Override public String toString() {  return JSONObject.toJSONString(this); }}

自定义全局异常处理类文章源自JAVA秀-https://www.javaxiu.com/11758.html

最后我们在来编写一个自定义全局异常处理的类。代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

@ControllerAdvicepublic class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /**  * 处理自定义的业务异常  * @param req  * @param e  * @return  */    @ExceptionHandler(value = BizException.class)      @ResponseBody   public  ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){     logger.error("发生业务异常!原因是:{}",e.getErrorMsg());     return ResultBody.error(e.getErrorCode(),e.getErrorMsg());    } /**  * 处理空指针的异常  * @param req  * @param e  * @return  */ @ExceptionHandler(value =NullPointerException.class) @ResponseBody public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){  logger.error("发生空指针异常!原因是:",e);  return ResultBody.error(CommonEnum.BODY_NOT_MATCH); }    /**        * 处理其他异常     * @param req     * @param e     * @return     */    @ExceptionHandler(value =Exception.class) @ResponseBody public ResultBody exceptionHandler(HttpServletRequest req, Exception e){     logger.error("未知异常!原因是:",e);        return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);    }}

因为这里我们只是用于做全局异常处理的功能实现以及测试,所以这里我们只需在添加一个实体类和一个控制层类即可。文章源自JAVA秀-https://www.javaxiu.com/11758.html

实体类文章源自JAVA秀-https://www.javaxiu.com/11758.html

又是万能的用户表 ()文章源自JAVA秀-https://www.javaxiu.com/11758.html

代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

public class User implements Serializable{ private static final long serialVersionUID = 1L; /** 编号 */  private int id;  /** 姓名 */  private String name;  /** 年龄 */  private int age;  public User(){  } public int getId() {  return id; } public void setId(int id) {  this.id = id; } public String getName() {  return name; } public void setName(String name) {  this.name = name; } public int getAge() {  return age; } public void setAge(int age) {  this.age = age; } public String toString() {  return JSONObject.toJSONString(this); }}

Controller 控制层文章源自JAVA秀-https://www.javaxiu.com/11758.html

控制层这边也比较简单,使用Restful风格实现的CRUD功能,不同的是这里我故意弄出了一些异常,好让这些异常被捕获到然后处理。这些异常中,有自定义的异常抛出,也有空指针的异常抛出,当然也有不可预知的异常抛出(这里我用类型转换异常代替),那么我们在完成代码编写之后,看看这些异常是否能够被捕获处理成功吧!文章源自JAVA秀-https://www.javaxiu.com/11758.html

代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

@RestController@RequestMapping(value = "/api")public class UserRestController { @PostMapping("/user")    public boolean insert(@RequestBody User user) {     System.out.println("开始新增...");     //如果姓名为空就手动抛出一个自定义的异常!        if(user.getName()==null){            throw  new BizException("-1","用户姓名不能为空!");        }        return true;    }    @PutMapping("/user")    public boolean update(@RequestBody User user) {     System.out.println("开始更新...");       //这里故意造成一个空指针的异常,并且不进行处理        String str=null;        str.equals("111");        return true;    }    @DeleteMapping("/user")    public boolean delete(@RequestBody User user)  {        System.out.println("开始删除...");        //这里故意造成一个异常,并且不进行处理        Integer.parseInt("abc123");        return true;    }    @GetMapping("/user")    public List<User> findByUser(User user) {     System.out.println("开始查询...");        List<User> userList =new ArrayList<>();        User user2=new User();        user2.setId(1L);        user2.setName("xuwujing");        user2.setAge(18);        userList.add(user2);        return userList;    }}

App 入口文章源自JAVA秀-https://www.javaxiu.com/11758.html

和普通的SpringBoot项目基本一样。文章源自JAVA秀-https://www.javaxiu.com/11758.html

代码如下:文章源自JAVA秀-https://www.javaxiu.com/11758.html

@SpringBootApplicationpublic class App {    public static void main( String[] args )    {  SpringApplication.run(App.class, args);  System.out.println("程序正在运行...");    }}

功能测试

我们成功启动该程序之后,使用Postman工具来进行接口测试。文章源自JAVA秀-https://www.javaxiu.com/11758.html

首先进行查询,查看程序正常运行是否ok,使用GET 方式进行请求。文章源自JAVA秀-https://www.javaxiu.com/11758.html

GET [http://localhost:8181/api/user](http://localhost:8181/api/user)文章源自JAVA秀-https://www.javaxiu.com/11758.html

返回参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"id":1,"name":"xuwujing","age":18}文章源自JAVA秀-https://www.javaxiu.com/11758.html

示例图:看看人家 SpringBoot 的全局异常处理,多么优雅…文章源自JAVA秀-https://www.javaxiu.com/11758.html

可以看到程序正常返回,并没有因自定义的全局异常而影响。文章源自JAVA秀-https://www.javaxiu.com/11758.html

然后我们再来测试下自定义的异常是否能够被正确的捕获并处理。文章源自JAVA秀-https://www.javaxiu.com/11758.html

使用POST方式进行请求文章源自JAVA秀-https://www.javaxiu.com/11758.html

POST [http://localhost:8181/api/user](http://localhost:8181/api/user)文章源自JAVA秀-https://www.javaxiu.com/11758.html

Body参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"id":1,"age":18}文章源自JAVA秀-https://www.javaxiu.com/11758.html

返回参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"code":"-1","message":"用户姓名不能为空!","result":null}文章源自JAVA秀-https://www.javaxiu.com/11758.html

示例图:文章源自JAVA秀-https://www.javaxiu.com/11758.html

看看人家 SpringBoot 的全局异常处理,多么优雅…文章源自JAVA秀-https://www.javaxiu.com/11758.html

可以看出将我们抛出的异常进行数据封装,然后将异常返回出来。文章源自JAVA秀-https://www.javaxiu.com/11758.html

然后我们再来测试下空指针异常是否能够被正确的捕获并处理。在自定义全局异常中,我们除了定义空指针的异常处理,也定义最高级别之一的Exception异常,那么这里发生了空指针异常之后,它是回优先使用哪一个呢?这里我们来测试下。文章源自JAVA秀-https://www.javaxiu.com/11758.html

使用PUT方式进行请求。文章源自JAVA秀-https://www.javaxiu.com/11758.html

PUT [http://localhost:8181/api/user](http://localhost:8181/api/user)文章源自JAVA秀-https://www.javaxiu.com/11758.html

Body参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"id":1,"age":18}文章源自JAVA秀-https://www.javaxiu.com/11758.html

返回参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"code":"400","message":"请求的数据格式不符!","result":null}文章源自JAVA秀-https://www.javaxiu.com/11758.html

示例图:看看人家 SpringBoot 的全局异常处理,多么优雅…文章源自JAVA秀-https://www.javaxiu.com/11758.html

我们可以看到这里的的确是返回空指针的异常护理,可以得出全局异常处理优先处理子类的异常。文章源自JAVA秀-https://www.javaxiu.com/11758.html

那么我们在来试试未指定其异常的处理,看该异常是否能够被捕获。文章源自JAVA秀-https://www.javaxiu.com/11758.html

使用DELETE方式进行请求。文章源自JAVA秀-https://www.javaxiu.com/11758.html

DELETE [http://localhost:8181/api/user](http://localhost:8181/api/user)文章源自JAVA秀-https://www.javaxiu.com/11758.html

Body参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"id":1}文章源自JAVA秀-https://www.javaxiu.com/11758.html

返回参数为:文章源自JAVA秀-https://www.javaxiu.com/11758.html

{"code":"500","message":"服务器内部错误!","result":null}文章源自JAVA秀-https://www.javaxiu.com/11758.html

看看人家 SpringBoot 的全局异常处理,多么优雅…文章源自JAVA秀-https://www.javaxiu.com/11758.html

这里可以看到它使用了我们在自定义全局异常处理类中的Exception异常处理的方法。到这里,测试就结束了。顺便再说一下,自义定全局异常处理除了可以处理上述的数据格式之外,也可以处理页面的跳转,只需在新增的异常方法的返回处理上填写该跳转的路径并不使用ResponseBody 注解即可。细心的同学也许发现了在GlobalExceptionHandler类中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它会将数据自动转换成JSON格式,这种于ControllerRestController类似,所以我们在使用全局异常处理的之后可以进行灵活的选择处理。文章源自JAVA秀-https://www.javaxiu.com/11758.html

来源 | https://www.cnblogs.com/xuwujing/p/10933082.html文章源自JAVA秀-https://www.javaxiu.com/11758.html

推荐阅读• 漫画TCP,一个悲伤的故事• 并发下的 HashMap 为什么会引起死循环???• Redis单线程已经很快了,为什么6.0要引入多线程?带来什么优势?• 使用雪花id或uuid作为Mysql主键,被老板怼了一顿!最近面试BATJ,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。文章源自JAVA秀-https://www.javaxiu.com/11758.html

谢谢支持哟 (*^__^*)文章源自JAVA秀-https://www.javaxiu.com/11758.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定