golang代码生成方案

为了更加贴合java开发的习惯,我自己开发了maven plugin,解析java代码并自动生成go代码,以及通过git submodule拉取golang的lib库,生成go代码后再推送至lib库上。

golang代码的格式设计

格式设计我按照java的开发习惯来,部分参考了protobuf的规定。

单独一个服务的结构如下:

java 与golang java与golang的相互调用_java

golang代码结构其中dto为使用的数据结构,enums中为简单枚举,service里则是调用方法。我将dubbo-go提供的调用方式进行了封装,以贴合更加常用的调用方法:

java 与golang java与golang的相互调用_java_02

OrderRpcService

可以看到,用户在使用方法时直接调用GetOrder(arg0 int64)方法即可,而不用关心默认方法里的参数内容,更加方便使用。

但是这样设计的格式有个问题:自己魔改的dubbo-go版本是在lib库springboot-dubbo-go的下一层,但是想要能够装配接口名与Url,必须先执行service中的config.SetConsumerService方法。因为这个问题,一度我将整个服务发现的过程迁移到了lib库中,但最终考虑到扩展多个lib库的情况,采用了以下措施:

java 与golang java与golang的相互调用_泛型_03

client.go

新增了一个client文件,在这里初始化时调用nacos_client的Init方法,同时引用api包,即自动生成的golang代码所在的包,以此来保证api包中的init方法在nacos_client的Init方法被调用之前就调用了。

java的泛型嵌套与重载

java有一些特性是golang所不支持的,比如泛型嵌套和重载。

泛型嵌套

我开发的代码生成插件中,支持的java泛型只有Map与List。从go请求java时,go将数据变为[]interface{},而java返回时,go如何以怎样的数据结构接收成了设计上的一个问题。使用[]interface{}接收的话,插件开发简单,但用户使用需要显式转换类型成本更高,使用数组的话则会碰到与Map一样的问题,java的泛型嵌套无法传递数据:比如List>是无法传递到[][]int32上的。在与同事咨询了他们protobuf的开发过程后了解到,他们是不会使用泛型嵌套的。因而我也采用了相同的方案:将List转换为数组并且不支持泛型嵌套。

重载

golang是不支持重载的,所以我在插件中进行了判断:如果service中存在重载方法,则跳过此service的生成。

其他

java中有一些特殊的类,比如LocalDatetime,官方没有提供其对于golang中time.Time的转换,所以遇到这种类我也直接跳过不生成dto及相关的service。

Maven插件的开发

maven插件开发相关可以移步至我的《maven插件开发》文章中查看。

结语

其实一开始决定要自己做这么个解决方案的时候还是很忐忑的,实际开发中的各种困难也确实证明了我的顾虑:没有接触过golang,需要一边百度一边请教同事来开发/阅读源码;没有开发过maven,遇到了不少插件开发的难题;dubbo-go遇到的2个bug我花了十多天才找到原因并进行处理,等等。好在结局不错,在开发的过程中也收获了不少。在此将实践经验进行了整理与分享,希望能够帮助更多的人。