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. nginx配置文件内容详解

    events { # 服务器最大链接数 worker_connections 1024; # 设置一个进程是否同时接受多个网络连接,默认为off multi_accept on; #事件驱动模型,se ...

  2. Elasticsearch中的一些重要概念:cluster, node, index, document, shards及replica

    首先,我们来看下一下如下的这个图: Cluster Cluster也就是集群的意思.Elasticsearch集群由一个或多个节点组成,可通过其集群名称进行标识.通常这个Cluster 的名字是可以在 ...

  3. OpenglEs之三角形绘制

    在前面我们已经在NDK层搭建好了EGL环境,也介绍了一些着色器相关的理论知识,那么这次我们就使用已经搭配的EGL绘制一个三角形吧. 在Opengl ES的世界中,无论多复杂的形状都是由点.线或三角形组 ...

  4. MES系统和ERP系统的区别是什么?

    首先得明白一点:MES(Manufacturing Execution System,即制造执行系统)系统跟ERP(Enterprise Resource Planning,企业资源计划)系统是两个完 ...

  5. WMS 相比于 ERP 系统有哪些优势?

    WMS与ERP系统是两个不同的系统,不存储优势的比较!WMS是仓库管理系统(Warehouse Management System) 的缩写,ERP是Enterprise Resource Plann ...

  6. HDU1114 Piggy-Bank (完全背包)

    完全背包模板,和01背包相比不用倒推,因为一种可以选多个. 这道题求最小,dp数组初始化为无穷即可. 1 #include<iostream> 2 #include<cstring& ...

  7. PHP Phar反序列化学习

    PHP Phar反序列化学习 Phar Phar是PHP的压缩文档,是PHP中类似于JAR的一种打包文件.它可以把多个文件存放至同一个文件中,无需解压,PHP就可以进行访问并执行内部语句. 默认开启版 ...

  8. 2021 CCPC 威海站 VP记录(题解)

    2021 CCPC 威海站 VP记录(题解) 题目顺序为vp时开题顺序: A - Goodbye, Ziyin! 签到,连边数小于等于2的可以作为二叉树根,若有大于4的直接输出0. code: voi ...

  9. http://localhost:8282/user/findsomeuser[object%20Object]

    查看vue中的方法的访问路径是否填写正确. 后端:

  10. 解决在vue中设置的height: 100%没有效果

    在新的页面设置height无效果的时候.需要改动App这个文件的heigth 解决办法.给app这个盒子设置高度.默认情况下为0 设置高度100%时,div的高度会等同于其父元素的高度.而上面中id为 ...