java 异常 返回值_优雅的异常和返回值处理

这样,开发者必须要通过给定的静态方法创建成果或者失败的Result;如果返回失败,那一定要通过IError去构建返回结构,code一定是IError里面定义的,失败信息可以是默认的也可以是我们自己定义的。

通过简单的四个类,将整个项目中的变量校验、异常定义和返回结果串联起来,统一处理,方便扩展。如果一个项目中大家都各自按照自己的习惯去定义异常、处理返回值,那么整个项目一定是无比的杂乱,让人抓狂。统一的异常处理和返回值定义是每个项目中必不可缺的一部分。

1、变量值的校验

项目中我们经常能遇到对一个方法的返回值判断是否为空,如:

List userPOList = queryUsers();

if (null == userPOList || userPOList.size() == 0) {

return null;

}

// 继续后面的逻辑

或者是这样:

List userPOList = queryUsers();

if (null == userPOList || userPOList.size() == 0) {

throw new RuntimeException("userPOList is empty");

}

// 继续后面的逻辑

亦或者是这样的:

List userPOList = queryUsers();

Assert.notNull(userPOList, "userPOList is empty");

// 继续后面的逻辑

这些写法本身是没有什么问题的,抛出异常后我们也可以通过统一的异常处理方式来捕获异常,再返回相应的异常文案即可。但是,我们可以使用一种更优雅的方式去定义异常和进行变量校验。

我们先定义一个IError接口,如下:

public interface IError {

int getCode();

String getMsg();

/**

* @descr: 判断对象是否为null

*/

default void ifNull(Object object, String msg) {

if (null == object) {

throw new RuntimeException(msg);

}

}

/**

* @descr: 判断对象是否为null

*/

default void ifNull(Object object) {

ifNull(object, this.getMsg());

}

/**

* @descr: 判断字符串是否为空

*/

default void ifEmpty(String str, String msg) {

if(null==str || "".equals(str.trim())) {

throw new RuntimeException(msg);

}

}

/**

* @descr: 判断字符串是否为空

*/

default void ifEmpty(String str) {

ifEmpty(str, this.getMsg());

}

/**

* @descr: 判断集合是否为null或者空

*/

default void ifEmpty(Collection> collection, String msg) {

if(null==collection || collection.size()==0) {

throw new RuntimeException(msg);

}

}

/**

* @descr: 判断集合是否为null或者空

*/

default void ifEmpty(Collection> collection) {

ifEmpty(collection, this.getMsg());

}

}

这里我们用到了java8的接口默认方法,实现了这个接口的任何对象,都会自动继承这些方法。我们再定义一个该接口的实现枚举Errors:

@AllArgsConstructor

public enum Errors implements IError {

/**

* 服务器异常

*/

SERVICE_ERROR(500, "服务器异常"),

/**

* 非法参数

*/

ILLEGAL_ARGUMENT(601, "非法参数");

@Getter

private int code;

@Getter

private String msg;

}

这时,我们就可以通过另一种方式去判断是否为空了:

List userPOList = queryUsers();

Errors.ILLEGAL_ARGUMENT.ifNull(userPOList, "userPOList is empty");

// 或者是这样

Errors.SERVICE_ERROR.ifNull(userPOList, "userPOList is empty");

这里看到的变量值的判断和我们使用Assert的判断并没有什么区别,只是Assert会抛出IllegalArgumentException异常而已。

没什么区别主要是因为我们抛出的是RuntimeException,并没有充分利用我们定义的枚举code值。如果我们抛出一个自定义异常,又会怎么样呢?

2、构建统一的异常

在一个项目中,我们期望只定义一个业务异常类,不同类型的异常通过异常code进行区分,而不希望为每种类型的业务异常都去定义一个异常类。只定义一个业务异常类,有利于我们做统一异常处理。所以,我们定义了一个业务异常类,如下:

public class BizException extends RuntimeException {

@Getter

private int code;

@Getter

private String msg;

public BizException(IError error) {

this.code = error.getCode();

this.msg = error.getMsg();

}

public BizException(IError error, String msg) {

this.code = error.getCode();

this.msg = msg;

}

}

这个业务异常类是以第一部分我们定义的IError作为参数,这样,我们就能获得Errors.ILLEGAL_ARGUMENT或者Errors.SERVICE_ERROR的code,从而可以区分这个业务异常的类型。所以IError接口中抛出RuntimeException的地方我们可以做如下修改:

/**

* @descr: 判断对象是否为null

*/

default void ifNull(Object object, String msg) {

if (null == object) {

throw new BizException(this, msg);

}

}

此时Errors.ILLEGAL_ARGUMENT.ifNull(userPOList, "userPOList is empty");抛出的异常即为:xxx.BizException: userPOList is empty。在枚举Errors中,我们已经定义了SERVICE_ERROR和ILLEGAL_ARGUMENT两个类型的异常。当我们需要定义其他异常时,只需要自定义一个枚举并实现IError接口即可。

3、构建统一的返回值

一般采用如下格式定义接口返回值:

// 成功

{"code":200,"msg":"成功","data":null}

// 失败

{"code":601,"msg":"userPOList is empty","data":null}

最简单的方式就是我们定义一个Result类,让所有接口都返回这个对象:

@Data

public class BaseResult implements Serializable {

private int code;

private String msg;

private T data;

}

我们只需要如下返回即可:

return new BaseResult<>(601, "userPOList is empty", null);

但是这样做有一个问题,如果有人返回一个code=60001但是我们又没有定义60001该怎么办呢?就是说,我们不能让开发者主动创建Result,而是提供静态方法供开发者使用。如下代码:

@Data

public class BaseResult implements Serializable {

private int code;

private String msg;

private T data;

private BaseResult() {

// 构造方法私有

}

private BaseResult(int code, String msg, T data) {

this.code = code;

this.msg = msg;

this.data = data;

}

/**

* @descr: 调用成功,无返回值

*/

public static BaseResult success() {

return success(null);

}

/**

* @descr: 调用成功,有返回值

*/

public static BaseResult success(T data) {

return new BaseResult<>(200, null, data);

}

/**

* @descr: 调用失败,返回Error信息

*/

public static BaseResult failed(IError error) {

return failed(error, error.getMsg());

}

/**

* @descr: 调用失败,返回自定义信息

*/

public static BaseResult failed(IError error, String msg) {

return failed(error, msg, null);

}

/**

* @descr: 调用失败,返回自定义信息

*/

public static BaseResult failed(IError error, String msg, Object data) {

return new BaseResult<>(error.getCode(), msg, data);

}

}

这样,开发者必须要通过给定的静态方法创建成果或者失败的Result;如果返回失败,那一定要通过IError去构建返回结构,code一定是IError里面定义的,失败信息可以是默认的也可以是我们自己定义的。

通过简单的四个类,将整个项目中的变量校验、异常定义和返回结果串联起来,统一处理,方便扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值