1. Result的使用

Result的使用,是java项目中开发接口的必备,它经常被我们用作接口的返回对象,方便前端或者其他程序的远程调用后处理业务。它一般包括以下几个属性:

  • code:一般根据系统设定,在接口调用中返回代表含义不同的编码(例如:code=2000代表接口调用正常,code=1000代表无权限 等等),而且项目中会有一个集中设定code的配置文件,方便根据编码查询错误具体的错误信息
  • message:调用接口时,返回的信息。一般接口正常调用返回"成功",调用失败返回失败原因,或者直接返回异常信息
  • data:调用接口后返回的数据,一般为使用 泛型T。例如:查询接口,data中即存放查询出来的数据。

Result.java

public class Result<T> implements Serializable {
// 标识代码,2000表示成功,其它数值表示出错
private int code;
// 返回的数据
private T data;
// 提示信息,供报错时使用
private String message;
// 返回时间
private String timestamp;
// 有参构造
public Result(int code, String message, T data) {
this.timestamp = String.valueOf(System.currentTimeMillis());
this.code = code;
this.data = data;
this.message = message;
this.traceId = traceId;
} // sucess、error两个方法方便Result的使用
public static <T> Result<T> success(T data) {
return new Result(ResultCode.OK.getCode(), ResultCode.OK.getMessage(), data);
}
public static <T> Result<T> error(ResultCode code) {
return new Result(code.getCode(), code.getMessage(), null);
}
}

ResultCode.java 用于定于各种状态码 以及 状态码的具体业务含义的描述

public enum ResultCode  {
UNAUTHORIZED(1000, "未经授权,无法访问"),
OK(2000, "成功"),
; private int code;
private String message; ResultCode(int code, String message) {
this.code = code;
this.message = message;
} public int getCode() {
return code;
} public String getMessage() {
return message;
}
}

2. 存在的问题

当我们在享受Result给我们带来的便利的同时,也在许多场景困惑于它带来的"麻烦"。java的远程调用遵循:使用起来要像在本地方法调用的感觉一样。

举个栗子

一个根据id查询用户的例子,使用Result作为返回结果

// 提供服务方
public interface UserService {
Result<User> getUserById(Long userId);
} // 调用例子
public User testGetUser(Long userId) {
String userKey = "userid-" + userId;
// 先查缓存,如果命中则返回缓存中的user
// cacheManager.get(123, userKey);
// ... try{
Result<User> reslut = userService.getUserById(userId);
if(result.isSuccess()) {
cacheManager.put(123, userKey, result.getData());
return reslut.getData();
}
// 否则清空缓存对象,代表用户不存在
cacheManager.put(123, userKey, NullCacheObject.getInstance(), 3600);
return null;
} catch(Exception e) {
// TODO log
throw new DemoException("getUserById error, userId:" + userId, e);
}
}

从result.isSuccess为fasle的地方思考,我们并分不清报错的实际含义:是因为网络异常、DB错误等系统错误导致、或是因为用户不存在等业务错误导致,从Result中取得的错误码,还要去服务方提供的状态码文档中去查找分析,如果是服务端系统异常,那么15行将导致后续1小时对该用户的请求都认为用户不存在。

严谨点的写法:

// 调用例子
public User testGetUser(Long userId) {
String userKey = "userid-" + userId;
// 先查缓存,如果命中则返回缓存中的user
// cacheManager.get(123, userKey);
// ... try{
Result<User> reslut = userService.getUserById(userId);
if(result.isSuccess()) {
cacheManager.put(123, userKey, result.getData());
return reslut.getData();
}
try{
if("USER_NOT_FOUND".equals(result.getCode())) {
// 清空缓存对象,代表用户不存在
cacheManager.put(123, userKey, NullCacheObject.getInstance(), 3600);
} else {
// 可能是SYSTEM_ERROE、DB_ERROR等系统异常
throw new DemoException("getUserById error, userId:" + userId + ",result=" + result );
}
} catch(DemoExpcetion e) {
throw e;
}
} catch(Exception e) {
// TODO log
throw new DemoException("getUserById error, userId:" + userId, e);
}
return null;
}

代码变得复杂起来。

下面是接口提供方直接将系统异常抛出,将业务异常封装的写法

public interface UserService{
User getUserById(Long userId) throws DemoAppException;
} // 调用例子
public User testGetUser(Long userId) {
String userKey = "userid-" + userId;
// 先查缓存,如果命中则返回缓存中的user
// cacheManager.get(123, userKey);
// ... try{
User user = userService.getUserById(userId);
if(user != null) {
cacheManager.put(123, userKey, user);
return user;
}
} catch(Exception e) {
// TODO log
throw new DemoException("getUserById error, userId:" + userId, e);
}
return null;
}

这样代码会比较简介,而且符合我们的调用习惯:是远程调用异常处理起来像本地异常一样,不然会导致异常的处理很混乱、复杂。


3. 总结

业务异常 与 系统异常要分清并不同化处理

  • 业务异常:例如:无权限、无此用户等,调用方会根据返回的不同异常code处理不同的业务,无权限会提醒"请分配权限",无此用户会提醒"先注册,再使用系统",此类异常封装为固定code是有价值所在,方便调用方。
  • 系统异常:例如:网络超时、DB异常等问题,对于此类异常掉用方并无能力处理此类异常,即使我们将此类异常用ResultCode封装起来后,其实调用方并不会根据我们返回的不同code去具体处理业务,而是用来日志定位、问题追踪。对于这类问题如果直接根据有无业务结果来判断,直接抛异常比Result封装异常后理解和使用上的更直接。同时也减少调用的资源开支。这个时候,我们在开发过程中就要能够准确地预测掉用方在接收到我们封装的异常后,会如何处理。

--该文章参考自《阿里淘系-2021技术人的百宝黑皮书》

java 如何正确使用接口返回对象Result的更多相关文章

  1. Java 利用枚举封装接口返回 JSON 结构

    利用枚举封装返回码和返回信息 package com.template.project.util; public enum StatusCode { SUCCESS(20000, "成功&q ...

  2. WebAPI接口返回ArrayList包含Dictionary对象正确解析

    一.问题提出 为了减少流量,将key-value(键值对)直接输出到Dictionary<string, string>,接口返回结果如下: 其中{}里面内容如下: 上图显示600是键,4 ...

  3. JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法

    在实际应用中经常会比较两个对象是否相等,比如下面的Address类,它有两个属性:String province 和 String city. public class Address { priva ...

  4. Effective Java 第三版——64. 通过对象的接口引用对象

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  5. java 多线程:Callable接口;FutureTask类实现对象【Thread、Runnable、Callable三种方式实现多线程的区别】

    Callable接口介绍: Java5开始,Java提供了Callable接口,像是Runnable接口的增强版,Callable接口提供了一个 call()方法可以作为线执行体. call()方法比 ...

  6. Java的Object.hashCode()的返回值到底是不是对象内存地址?

    关于这个问题,查阅了网上的资料,发现证明过程太繁琐,这里我用了反证法. java.lang.Object.hashCode()的返回值到底是不是对象内存地址? hashCode契约 说到这个问题,大家 ...

  7. 《Java核心技术》 -- 读书笔记 ② - 类 | 对象 | 接口

    对象vs对象变量 “对象” 描述的是一个类的具体实例,他被java虚拟机分配在 "堆" (Heap)中. “对象变量” 为一个对象的引用(对象变量的值=记载着具体对象的位置/地址) ...

  8. java解析从接口获取的json内容并写到excle(只写与标题匹配的值,并非把所有的接口返回值都写进去)

    需求:从接口中获取的一个json数组中有多个对象,每个对象中的值并非都需要,只需查出标题中的几项对应的值即可.且还需要按某个字段排序后依次写到excel 实现方法如下: package jansonD ...

  9. 为什么阿里巴巴Java开发手册中强制要求接口返回值不允许使用枚举?

    在阅读<阿里巴巴Java开发手册>时,发现有一条关于二方库依赖中接口返回值不允许使用枚举类型的规约,具体内容如下: 在谈论为什么之前先来科普下什么是二方库,二方库也称作二方包,一般指公司内 ...

  10. Java中的Serializable接口transient关键字,及字节、字符、对象IO

    1.什么是序列化和反序列化Serialization是一种将对象转为为字节流的过程:deserialization是将字节流恢复为对象的过程. 2.什么情况下需要序列化a)当你想把的内存中的对象保存到 ...

随机推荐

  1. Redis高并发分布式锁详解

    为什么需要分布式锁 1.为了解决Java共享内存模型带来的线程安全问题,我们可以通过加锁来保证资源访问的单一,如JVM内置锁synchronized,类级别的锁ReentrantLock. 2.但是随 ...

  2. 二手商城集成jwt认证授权

    ------------恢复内容开始------------ 使用jwt进行认证授权的主要流程 参考博客(https://www.cnblogs.com/RayWang/p/9536524.html) ...

  3. day09-2视图和用户权限

    视图和用户权限 1.视图(view) 看一个需求 emp表的列信息很多,有些信息是个人重要信息(比如:sal.comm.mgr.hiredate),如果我们希望某个用户只能查询emp表的empno.e ...

  4. virtualbox的Linux虚拟磁盘大小调整及注意事项

    virtualBox 调整磁盘分区 起因 起初安装centos6.3 时,没有修改默认的硬盘空间.只有8G,导致后面安装完zookeeper,jdk之后,在安装mysql发现磁盘空间不足 扩容步骤 1 ...

  5. Java8新特性之Stream流(含具体案例)

    一.概述   Stream 流是 Java 8 新提供给开发者的一组操作集合的 API,将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选.排序.聚合等.元素 ...

  6. Java中的名称命名规范

    包名:多单词组成时所有字母都小写:xxxyyyzzz 类名.接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz 变量名.方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首 ...

  7. laravel 浏览器谷歌network返回报错html

    laravel 在谷歌报错的时候会返回html,对于调试来说很不方便.原因是在于: 这里返回的格式是json,但是报错时候返回的是整个html所以 相对路径: app\Exceptions\Handl ...

  8. 那齐博x3又什么什么?

    那齐博x3又什么什么? 齐博x3是齐博X1/齐博x2之后的升级版本. 主要优化圈子系统

  9. Go | 基本数据类型的相互转换

    基本数据类型的相互转换 Go在不同类型的变量之间赋值时需要显示转换,不能自动转换 基本语法 表达式 T(v): 将值v转换成类型T T就是数据类型: int32, int64, float32... ...

  10. CentOS 7.9 Related Software Directory

    一.CentOS 7.9 Related Software Directory Installing VMware Workstation Pro on Windows Installing Cent ...