点击上方 好好学java ,选择 星标 公众号

重磅资讯、干货,第一时间送达

今日推荐:今天给大家推荐 6 个 Spring Boot 项目,拿来就可以赚钱!

 个人原创100W+访问量博客:点击前往,查看更多

作者 l Hollis

来源 l Hollis(ID:hollischuang)

最近,我们的线上环境出现了一个问题,线上代码在执行过程中抛出了一个IllegalArgumentException,分析堆栈后,发现最根本的的异常是以下内容:

java.lang.IllegalArgumentException: No enum constant com.a.b.f.m.a.c.AType.P_M

大概就是以上的内容,看起来还是很简单的,提示的错误信息就是在AType这个枚举类中没有找到P_M这个枚举项。

于是经过排查,我们发现,在线上开始有这个异常之前,该应用依赖的一个下游系统有发布,而发布过程中是一个API包发生了变化,主要变化内容是在一个RPC接口的Response返回值类中的一个枚举参数AType中增加了P_M这个枚举项。

但是下游系统发布时,并未通知到我们负责的这个系统进行升级,所以就报错了。

我们来分析下为什么会发生这样的情况。

问题重现

首先,下游系统A提供了一个二方库的某一个接口的返回值中有一个参数类型是枚举类型。

一方库指的是本项目中的依赖

二方库指的是公司内部其他项目提供的依赖

三方库指的是其他组织、公司等来自第三方的依赖

public interface AFacadeService {public AResponse doSth(ARequest aRequest);}public Class AResponse{private Boolean success;private AType aType;}public enum AType{P_T,A_B}

然后B系统依赖了这个二方库,并且会通过RPC远程调用的方式调用AFacadeService的doSth方法。

public class BService {@AutowiredAFacadeService aFacadeService;public void doSth(){ARequest aRequest = new ARequest();AResponse aResponse = aFacadeService.doSth(aRequest);AType aType = aResponse.getAType();}}

这时候,如果A和B系统依赖的都是同一个二方库的话,两者使用到的枚举AType会是同一个类,里面的枚举项也都是一致的,这种情况不会有什么问题。

但是,如果有一天,这个二方库做了升级,在AType这个枚举类中增加了一个新的枚举项P_M,这时候只有系统A做了升级,但是系统B并没有做升级。

那么A系统依赖的的AType就是这样的:

public enum AType{P_T,A_B,P_M}

而B系统依赖的AType则是这样的:

public enum AType{P_T,A_B}

这种情况下,在B系统通过RPC调用A系统的时候,如果A系统返回的AResponse中的aType的类型为新增的P_M时候,B系统就会无法解析。一般在这种时候,RPC框架就会发生反序列化异常。导致程序被中断。

原理分析

这个问题的现象我们分析清楚了,那么再来看下原理是怎样的,为什么出现这样的异常呢。

其实这个原理也不难,这类RPC框架大多数会采用JSON的格式进行数据传输,也就是客户端会将返回值序列化成JSON字符串,而服务端会再将JSON字符串反序列化成一个Java对象。

而JSON在反序列化的过程中,对于一个枚举类型,会尝试调用对应的枚举类的valueOf方法来获取到对应的枚举。

而我们查看枚举类的valueOf方法的实现时,就可以发现,如果从枚举类中找不到对应的枚举项的时候,就会抛出IllegalArgumentException

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {T result = enumType.enumConstantDirectory().get(name);if (result != null)return result;if (name == null)throw new NullPointerException("Name is null");throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);}

关于这个问题,其实在《阿里巴巴Java开发手册》中也有类似的约定:

这里面规定"对于二方库的参数可以使用枚举,但是返回值不允许使用枚举"。这背后的思考就是本文上面提到的内容。

扩展思考

为什么参数中可以有枚举?

不知道大家有没有想过这个问题,其实这个就和二方库的职责有点关系了。

一般情况下,A系统想要提供一个远程接口给别人调用的时候,就会定义一个二方库,告诉其调用方如何构造参数,调用哪个接口。

而这个二方库的调用方会根据其中定义的内容来进行调用。而参数的构造过程是由B系统完成的,如果B系统使用到的是一个旧的二方库,使用到的枚举自然是已有的一些,新增的就不会被用到,所以这样也不会出现问题。

比如前面的例子,B系统在调用A系统的时候,构造参数的时候使用到AType的时候就只有P_T和A_B两个选项,虽然A系统已经支持P_M了,但是B系统并没有使用到。

如果B系统想要使用P_M,那么就需要对该二方库进行升级。

但是,返回值就不一样了,返回值并不受客户端控制,服务端返回什么内容是根据他自己依赖的二方库决定的。

但是,其实相比较于手册中的规定,我更加倾向于,在RPC的接口中入参和出参都不要使用枚举。

一般,我们要使用枚举都是有几个考虑:

  • 1、枚举严格控制下游系统的传入内容,避免非法字符。

  • 2、方便下游系统知道都可以传哪些值,不容易出错。

不可否认,使用枚举确实有一些好处,但是我不建议使用主要有以下原因:

  • 1、如果二方库升级,并且删除了一个枚举中的部分枚举项,那么入参中使用枚举也会出现问题,调用方将无法识别该枚举项。

  • 2、有的时候,上下游系统有多个,如C系统通过B系统间接调用A系统,A系统的参数是由C系统传过来的,B系统只是做了一个参数的转换与组装。这种情况下,一旦A系统的二方库升级,那么B和C都要同时升级,任何一个不升级都将无法兼容。

我其实建议大家在接口中使用字符串代替枚举,相比较于枚举这种强类型,字符串算是一种弱类型。

如果使用字符串代替RPC接口中的枚举,那么就可以避免上面我们提到的两个问题,上游系统只需要传递字符串就行了,而具体的值的合法性,只需要在A系统内自己进行校验就可以了。

为了方便调用者使用,可以使用javadoc的@see注解表明这个字符串字段的取值从那个枚举中获取。

public Class AResponse{private Boolean success;/***  @see AType */private String aType;}

对于像阿里这种比较庞大的互联网公司,随便提供出去的一个接口,可能有上百个调用方,而接口升级也是常态,我们根本做不到每次二方库升级之后要求所有调用者跟着一起升级,这是完全不现实的,并且对于有些调用者来说,他用不到新特性,完全没必要做升级。

还有一种看起来比较特殊,但是实际上比较常见的情况,就是有的时候一个接口的声明在A包中,而一些枚举常量定义在B包中,比较常见的就是阿里的交易相关的信息,订单分很多层次,每次引入一个包的同时都需要引入几十个包。

对于调用者来说,我肯定是不希望我的系统引入太多的依赖的,一方面依赖多了会导致应用的编译过程很慢,并且很容易出现依赖冲突问题。

所以,在调用下游接口的时候,如果参数中字段的类型是枚举的话,那我没办法,必须得依赖他的二方库。但是如果不是枚举,只是一个字符串,那我就可以选择不依赖。

所以,我们在定义接口的时候,会尽量避免使用枚举这种强类型。规范中规定在返回值中不允许使用,而我自己要求更高,就是即使在接口的入参中我也很少使用。

最后,我只是不建议在对外提供的接口的出入参中使用枚举,并不是说彻底不要用枚举,我之前很多文章也提到过,枚举有很多好处,我在代码中也经常使用。所以,切不可因噎废食。

当然,文中的观点仅代表我个人,具体是是不是适用其他人,其他场景或者其他公司的实践,需要读者们自行分辨下,建议大家在使用的时候可以多思考一下。

推荐文章
  • 今天给大家推荐 6 个 Spring Boot 项目,拿来就可以赚钱!

  • 交公粮了:我经常逛的技术网站

  • 圈子哥推荐一款基于 Spring Boot 开发 OA 开源产品,学习/搞外快都是不二选择!

  • 硬刚一周,3W字总结,一年的经验告诉你如何准备校招!

原创电子书历时整整一年总结的 Java 面试 + Java 后端技术学习指南,这是本人这几年及校招的总结,各种高频面试题已经全部进行总结,按照章节复习即可,已经拿到了大厂offer。
原创思维导图扫码或者微信搜 程序员的技术圈子 回复 面试 领取原创电子书和思维导图。

为什么阿里巴巴RPC接口不允许使用枚举类型?相关推荐

  1. unity的C#学习——foreach循环相关:IEnumerable接口、LINQ语言集成查询、可枚举对象与枚举类型

    文章目录 C#里的判断和循环 1.foreach循环 2.IEnumerable接口 3.LINQ语言集成查询 4.可枚举对象和枚举类型的区别 C#里的判断和循环 C#和C语言都有条件语句和循环语句, ...

  2. java基础(十一) 枚举类型

    枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...

  3. java基础篇(11) 枚举类型

    枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...

  4. 设计RPC接口时,你有考虑过这些吗?

    RPC 框架的讨论一直是各个技术交流群中的热点话题,阿里的 dubbo,新浪微博的 motan,谷歌的 grpc,以及不久前蚂蚁金服开源的 sofa,都是比较出名的 RPC 框架.RPC 框架,或者一 ...

  5. desc 枚举类型id_枚举系列(四):实现接口的枚举类

    枚举类也可以实现一个或者多个接口.与普通实现一个或者多个接口完全一样,枚举类实现一个或者多个接口时,也需要实现该接口所包含的方法.下面程序定义了一个GenderDesc接口. public inter ...

  6. 转载-- http接口、api接口、RPC接口、RMI、webservice、Restful等概念

    http接口.api接口.RPC接口.RMI.webservice.Restful等概念 收藏 Linux一叶 https://my.oschina.net/heavenly/blog/499661 ...

  7. thrift RPC接口请求超时

    某次client调用服务端thrift  RPC接口超时导致连接断开,但是server说自己返回数据了,然后client用tcpdump抓包发现没抓到server返回的数据,没抓到表明client没收 ...

  8. android app通过Geth RPC接口实现远程调用

    记录一下APP怎么通过以太坊的RPC接口实现远程调用.此过程的环境为Window7和Android studio2.2.3.以web3_clientVersion为例.更多用法参考Geth JSON- ...

  9. Effective Java之用接口模拟可伸缩的枚举(三十四)

    在实际工作中,我们常常会遇到这样的情况: 如果我在一个枚举类型封装在一个jar库里后,其他开发人员如果对该枚举进行扩展? 毕竟我刚开始定义的枚举并不是会涵盖所有的可能! public enum Mod ...

最新文章

  1. 2018-01-11 Antlr4实现数学四则运算
  2. Python 进阶_函数式编程
  3. html px转换,pc端px转换为rem针对屏幕分辨率进行页面适配
  4. 基于深度学习的语义分割代码库
  5. osg节点访问和遍历
  6. 关于标准库中的ptr_fun/binary_function/bind1st/bind2nd
  7. 自然语言交流系统 phxnet团队 创新实训 项目博客 (五)
  8. java.sql.SQLException: ORA-00604: 递归 SQL 级别 1 出现错误
  9. 【Arduino】OTTO机器人(做二次开发的一点点总结)
  10. logstash 启动方式
  11. Web应用程序框架 Apache Click
  12. matlab fig图片读取,从Matlab .fig文件中读取数据,并重新绘图
  13. 电子计算机 划分发展阶段,电子计算机主要是以( )为标志来划分发展阶段的
  14. matlab霍夫曼吗仿真,霍夫曼编译码的Matlab代码实现
  15. GBase 8c亮相国内首款金融数据库性能测试工具开源发布会
  16. 平安好医生上半年营收28亿:同比降26% 净亏4.26亿
  17. 华为鸿蒙2.0来了,挑战谷歌安卓APP成关键?
  18. 模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉
  19. Issues:Frequency out of range: (expecting between 112.50Hz and 275.00Hz, measured 99.85Hz)
  20. [AV1] AV1 Specification

热门文章

  1. HDU1020 Encoding
  2. More Effective C# Item3 : 运行时检查泛型参数的类型并提供特定的算法
  3. AB1601 烧写程序后上电后无反应
  4. python常用库 自动化办公类 —— PyPDF2(处理pdf文件)
  5. DRF_APIView之认证、授权配置
  6. [Skill]-Markdown编辑器技巧
  7. Kali 渗透测试—Metasploit
  8. 网鼎杯2020 朱雀部分writeup
  9. 进程间通信——匿名管道
  10. 逆向寒假生涯(27/100)