乐趣区

关于java:Spring-Boot-全局异常处理这样写才优雅

本文首发于 xuwujing’s Notes 博客 \
地址:http://www.panchengming.com/

本篇文章次要介绍的是 SpringBoot 我的项目进行全局异样的解决。

SpringBoot 全局异样筹备

阐明:如果想间接获取工程那么能够间接跳到底部,通过链接下载工程代码。

开发筹备

环境要求:

JDK:1.8

SpringBoot:1.5.17.RELEASE

首先还是 Maven 的相干依赖:

<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>

配置文件这块根本不须要更改,全局异样的解决只需在代码中实现即可。

代码编写

SpringBoot 的我的项目曾经对有肯定的异样解决了,然而对于咱们开发者而言可能就不太适合了,因而咱们须要对这些异样进行对立的捕捉并解决。

SpringBoot 中有一个 ControllerAdvice 的注解,应用该注解示意开启了全局异样的捕捉,咱们只需在自定义一个办法应用 ExceptionHandler 注解而后定义捕捉异样的类型即可对这些捕捉的异样进行对立的解决。

咱们依据上面的这个示例来看该注解是如何应用吧。Spring Boot 根底就不介绍了,看睛这个:https://github.com/javastacks…

示例代码:

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

上述的示例中,咱们对捕捉的异样进行简略的二次解决,返回异样的信息,尽管这种可能让咱们晓得异样的起因,然而在很多的状况下来说,可能还是不够人性化,不合乎咱们的要求。

那么咱们这里能够通过自定义的异样类以及枚举类来实现咱们想要的那种数据吧。

自定义根底接口类

首先定义一个根底的接口类,自定义的谬误形容枚举类需实现该接口。

代码如下:

public interface BaseErrorInfoInterface {
    /** 错误码 */
     String getResultCode();
    
    /** 谬误形容 */
     String getResultMsg();}

自定义枚举类

而后咱们这里在自定义一个枚举类,并实现该接口。

代码如下:

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;}

}

自定义异样类

而后咱们在来自定义一个异样类,用于解决咱们产生的业务异样。

代码如下:

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;}

}

自定义数据格式

顺便这里咱们定义一下数据的传输格局。

代码如下:

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);
    }

}

自定义全局异样解决类

最初咱们在来编写一个自定义全局异样解决的类。

代码如下:

@ControllerAdvice
public 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);
    }
}

因为这里咱们只是用于做全局异样解决的性能实现以及测试,所以这里咱们只需在增加一个实体类和一个管制层类即可。

实体类

又是万能的用户表 ()

代码如下:

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 管制层

管制层这边也比较简单,应用 Restful 格调实现的 CRUD 性能,不同的是这里我成心弄出了一些异样,好让这些异样被捕捉到而后解决。这些异样中,有自定义的异样抛出,也有空指针的异样抛出,当然也有不可预知的异样抛出(这里我用类型转换异样代替),那么咱们在实现代码编写之后,看看这些异样是否可能被捕捉解决胜利吧!

代码如下:

@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 入口

和一般的 SpringBoot 我的项目根本一样。

代码如下:

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

功能测试

咱们胜利启动该程序之后,应用 Postman 工具来进行接口测试。

首先进行查问,查看程序失常运行是否 ok,应用 GET 形式进行申请。

GET http://localhost:8181/api/user

返回参数为:

{“id”:1,”name”:”xuwujing”,”age”:18}

示例图:

能够看到程序失常返回,并没有因自定义的全局异样而影响。

而后咱们再来测试下自定义的异样是否可能被正确的捕捉并解决。

应用 POST 形式进行申请

POST http://localhost:8181/api/user

Body 参数为:

{“id”:1,”age”:18}

返回参数为:

{“code”:”-1″,”message”:” 用户姓名不能为空!”,”result”:null}

示例图:

能够看出将咱们抛出的异样进行数据封装,而后将异样返回进去。

而后咱们再来测试下空指针异样是否可能被正确的捕捉并解决。在自定义全局异样中,咱们除了定义空指针的异样解决,也定义最高级别之一的 Exception 异样,那么这里产生了空指针异样之后,它是回优先应用哪一个呢?这里咱们来测试下。

应用 PUT 形式进行申请。

PUT http://localhost:8181/api/user

Body 参数为:

{“id”:1,”age”:18}

返回参数为:

{“code”:”400″,”message”:” 申请的数据格式不符!”,”result”:null}

示例图:

咱们能够看到这里的确实是返回空指针的异样护理,能够得出全局异样解决优先解决子类的异样。

那么咱们在来试试未指定其异样的解决,看该异样是否可能被捕捉。

应用 DELETE 形式进行申请。

DELETE http://localhost:8181/api/user

Body 参数为:

{“id”:1}

返回参数为:

{“code”:”500″,”message”:” 服务器外部谬误!”,”result”:null}

这里能够看到它应用了咱们在自定义全局异样解决类中的 Exception 异样解决的办法。

到这里,测试就完结了。

顺便再说一下,自义定全局异样解决除了能够解决上述的数据格式之外,也能够解决页面的跳转,只需在新增的异样办法的返回解决上填写该跳转的门路并不应用ResponseBody 注解即可。

仔细的同学兴许发现了在 GlobalExceptionHandler 类中应用的是 ControllerAdvice 注解,而非 RestControllerAdvice 注解,如果是用的 RestControllerAdvice 注解,它会将数据主动转换成 JSON 格局,这种于 ControllerRestController相似,所以咱们在应用全局异样解决的之后能够进行灵便的抉择解决。
近期热文举荐:

1.600+ 道 Java 面试题及答案整顿(2021 最新版)

2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

退出移动版