RES内存占用比jvm大很多 rec内存为0_开发者


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

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

1、变量值的校验

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


List<UserPO> userPOList = queryUsers();
if (null == userPOList || userPOList.size() == 0) {
    return null;
}
// 继续后面的逻辑


或者是这样:


List<UserPO> userPOList = queryUsers();
if (null == userPOList || userPOList.size() == 0) {
    throw new RuntimeException("userPOList is empty");
}
// 继续后面的逻辑


亦或者是这样的:


List<UserPO> 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<UserPO> 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<T> 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<T> 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 <T> BaseResult<T> success() {
        return success(null);
    }

    /**
     * @descr: 调用成功,有返回值
     */
    public static <T> BaseResult<T> success(T data) {
        return new BaseResult<>(200, null, data);
    }

    /**
     * @descr: 调用失败,返回Error信息
     */
    public static BaseResult<Object> failed(IError error) {
        return failed(error, error.getMsg());
    }

    /**
     * @descr: 调用失败,返回自定义信息
     */
    public static BaseResult<Object> failed(IError error, String msg) {
        return failed(error, msg, null);
    }

    /**
     * @descr: 调用失败,返回自定义信息
     */
    public static BaseResult<Object> failed(IError error, String msg, Object data) {
        return new BaseResult<>(error.getCode(), msg, data);
    }
}


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

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