类Status

注:下面内容翻译自 类Status 的 javadoc

通过提供标准状态码并结合可选的描述性的信息来定义操作的状态。状态的实例创建是通过以合适的状态码开头并补充额外信息:Status.NOT_FOUND.withDescription(“Could not find ‘important_file.txt’”);

对于客户端,每个远程调用在完成时都将返回一个状态。如果发生错误,这个状态将以RuntimeException的方式传播到阻塞桩(blocking stubs),或者作为一个明确的参数给到监听器。

同样的,服务器可以通过抛出 StatusRuntimeException 或者 给回掉传递一个状态来报告状态。

提供工具方法来转换状态到exception并从exception中解析出状态。

类Status.Code

状态码定义

在Status类中,通过内部类 Status.Code 以标准java枚举的方式定义了状态码:

  1. public final class Status {
  2. public enum Code {
  3. OK(0),
  4. CANCELLED(1),
  5. ......
  6. UNAUTHENTICATED(16);
  7. }
  8. }

这里定了17个(OK和其他16个错误)状态码,它们的详细定义和描述请见 状态码详细定义.

状态码的属性

Status.Code 有两个属性,类型为整型的value和对应的字符串表示方式的valueAscii:

  1. public enum Code {
  2. ......
  3. private final int value;
  4. private final String valueAscii;
  5. private Code(int value) {
  6. this.value = value;
  7. this.valueAscii = Integer.toString(value);
  8. }
  9. public int value() {
  10. return value;
  11. }
  12. private String valueAscii() {
  13. return valueAscii;
  14. }
  15. }

Code的status()方法

类Status.Code的status()方法通过使用 STATUS_LIST 来直接返回该状态码对应的 Status 对象:

  1. private Status status() {
  2. return STATUS_LIST.get(value);
  3. }

类Status

STATUS_LIST

静态变量 STATUS_LIST 保存了所有的 Status 对象,对应每个 Status.Code 枚举定义:

  1. // Create the canonical list of Status instances indexed by their code values.
  2. private static final List<Status> STATUS_LIST = buildStatusList();
  3. private static List<Status> buildStatusList() {
  4. TreeMap<Integer, Status> canonicalizer = new TreeMap<Integer, Status>();
  5. // 将所有的Code的枚举定义都游历
  6. for (Code code : Code.values()) {
  7. Status replaced = canonicalizer.put(code.value(), new Status(code));
  8. if (replaced != null) {
  9. //去重处理,有些奇怪这里可能重复吗?输入的可是枚举定义,value按说不会定义错
  10. throw new IllegalStateException("Code value duplication between "
  11. + replaced.getCode().name() + " & " + code.name());
  12. }
  13. }
  14. return Collections.unmodifiableList(new ArrayList<Status>(canonicalizer.values()));
  15. }

直接用value做下标,因此枚举定义的value就是从0开始。

Status的静态实例

Status中为每个 Status.Code 定义了一对一的静态的Status实例:

  1. public static final Status OK = Code.OK.status();
  2. public static final Status CANCELLED = Code.CANCELLED.status();
  3. ......
  4. public static final Status DATA_LOSS = Code.DATA_LOSS.status();

注意 Code.status() 方法是调 STATUS_LIST.get(value) 来获取 Status 实例。

Status的属性和构造函数

Status的属性,注意都是final不可变:

  1. // code 用来保存状态码
  2. private final Code code;
  3. // description 是状态描述信息
  4. private final String description;
  5. // cause 是关联的异常
  6. private final Throwable cause;
  7. private Status(Code code) {
  8. // description 和 cause 都可以为null
  9. this(code, null, null);
  10. }
  11. private Status(Code code, @Nullable String description, @Nullable Throwable cause) {
  12. this.code = checkNotNull(code);
  13. this.description = description;
  14. this.cause = cause;
  15. }

注意Status是注明 @Immutable 的,而且这个类是 final,不可以继承:

  1. @Immutable
  2. public final class Status {}

构造 Status 对象的静态方法

  1. 静态方法fromCodeValue()根据给定的状态码构建Status对象:

    1. public static Status fromCodeValue(int codeValue) {
    2. if (codeValue < 0 || codeValue > STATUS_LIST.size()) {
    3. return UNKNOWN.withDescription("Unknown code " + codeValue);
    4. } else {
    5. return STATUS_LIST.get(codeValue);
    6. }
    7. }

    对于有效的 code 值(0到 STATUS_LIST.size()),直接返回对应的保存在 STATUS_LIST 中的实例,这样得到的实例和前面的静态定义实际是同一个实例。

    注意:返回的 Status 实例中 description 和 cause 属性都是null。

  2. 静态方法 fromCode() 根据给定的Code对象构建Status对象:

    1. public static Status fromCode(Code code) {
    2. return code.toStatus();
    3. }
    4. //而toStatus()的实现非常简单,直接从 STATUS_LIST 里面取值。
    5. public enum Code {
    6. public Status toStatus() {
    7. return STATUS_LIST.get(value);
    8. }
    9. }
  3. 静态方法 fromThrowable() 根据给定的 Throwable 对象构建Status对象:

    1. public static Status fromThrowable(Throwable t) {
    2. Throwable cause = checkNotNull(t);
    3. // 循环渐进,逐层检查
    4. while (cause != null) {
    5. if (cause instanceof StatusException) {
    6. //StatusException就直接取status属性
    7. return ((StatusException) cause).getStatus();
    8. } else if (cause instanceof StatusRuntimeException) {
    9. //StatusRuntimeException也是直接取status属性
    10. return ((StatusRuntimeException) cause).getStatus();
    11. }
    12. //不是的话就继续检查cause
    13. cause = cause.getCause();
    14. }
    15. //最后如果还是找不到任何Status,就只能给 UNKNOWN
    16. return UNKNOWN.withCause(t);
    17. }

修改 Status 对象的属性

由于 Status 对象是不可变的,因此如果需要修改属性,只能重新构建一个新的实例。

  1. code 属性通常不需要修改
  2. 设置 cause 属性

    1. // 使用给定 cause 创建派生的 Status 实例。
    2. // 但是不管如何,cause 不会从服务器传递到客户端
    3. public Status withCause(Throwable cause) {
    4. if (Objects.equal(this.cause, cause)) {
    5. return this;
    6. }
    7. return new Status(this.code, this.description, cause);
    8. }
  3. 设置 description 属性

    1. // 使用给定 description 创建派生的 Status 实例。
    2. public Status withDescription(String description) {
    3. if (Objects.equal(this.description, description)) {
    4. return this;
    5. }
    6. return new Status(this.code, description, this.cause);
    7. }

    也可以在现有的 description 属性基础上增加额外的信息:

    1. // 在现有 description 的基础上增加额外细节来创建派生的 Status 实例。
    2. public Status augmentDescription(String additionalDetail) {
    3. if (additionalDetail == null) {
    4. return this;
    5. } else if (this.description == null) {
    6. // 如果原来 description 为null,直接设置
    7. return new Status(this.code, additionalDetail, this.cause);
    8. } else {
    9. // 如果原来 description 不为null,通过"\n"连接起来
    10. return new Status(this.code, this.description + "\n" + additionalDetail, this.cause);
    11. }
    12. }

Status 的方法

  1. isOk() 简单判断是否OK

    1. public boolean isOk() {
    2. return Code.OK == code;
    3. }
  2. asRuntimeException() 方法将当前 Status 对象转为一个 RuntimeException

    1. public StatusRuntimeException asRuntimeException() {
    2. return new StatusRuntimeException(this);
    3. }

    注意这里得到的 StatusRuntimeException 对象携带了当前 Status ,可以通过方法 fromThrowable() 找回这个 Status 实例。

  3. 带跟踪元数据的 asRuntimeException() 方法将当前 Status 对象转为一个 RuntimeException并携带跟踪元数据

    1. public StatusRuntimeException asRuntimeException(Metadata trailers) {
    2. return new StatusRuntimeException(this, trailers);
    3. }
  4. asException()功能类似,但是得到的是 Exception