poroto3 坑 枚举_案例篇:利用ProtoBuf文件,一键生成Java代码

本文介绍了如何使用Poroto3处理ProtoBuf文件,以生成Java代码并解决在Android开发中遇到的问题。文章通过案例详细阐述了ProtoBuf定义、实体类、枚举类和接口的生成,以及如何进行异步封装,同时探讨了如何兼容GRPC,包括模拟GRPC数据类和请求执行类。最后,作者总结了代码自动生成在减少重复工作和提高效率上的价值。
摘要由CSDN通过智能技术生成

1.背景2. 案例ProtoBuf定义生成的实体类生成的枚举类生成的接口类3. 异步封装生成的异步接口生成异步接口的实现类生成的异步接口的测试类4. 兼容GRPC生成的GRPC数据类-模拟生成的GRPC请求执行类-模拟生成的GRPC数据转换类总结

1.背景

在我们日常的开发中,前后端之间的接口联调很麻烦,经常出现后端加了字段,前端还不知道,所谓接口文档,经常和代码是不同步的。

好在现在有了grpc,它可以定义好ProtoBuf接口文件后,自动生成代码。原理就是RPC,在客户端生成一个远程服务的代理,可以像访问访问本地方法一样,访问远程方法。

但是,在客户端使用grpc的实践中,我们发现了grpc有几个弊端:

生成的实体类太大,很占内存,所以,我们最好只是用grpc的实体类来传输,但是在业务层,还是使用自己写的实体类更方便一点

自动生成的同步方法和异步方法,都不够好用,使用起来还是有一点点麻烦的

grpc生成的类都是final类,不便修改和继承

grpc生成的类没有实现Parcelable和Serializable接口,没法通过Intent传递,这在不同页面之间传递数据比较麻烦

为了解决grpc的弊端,我们最好在业务层自己封装一套实体类,grpc实体类只负责传输层,但是这又带来了一个新的问题:两套实体类之间互相转换的问题。

在我们的应用中,实体类的数量已经有接近800个,服务也有100多个。因为历史遗留问题,现在这些grpc的类还是广泛存在于业务层,迁移的话,非常麻烦。

经过一段时间的研究,我终于想到了一个方案:利用ProtoBuf接口文件,一键生成Java代码。

实现过程中,遇到了几个问题:

如何解析ProtoBuf文件?

因为我主要是玩Java的,所以还是想用Java代码来解析,但是在网上找了半天,好像都没有直接用Java解析ProtoBuf的方案。好在有一个替代方案,先将proto文件编译成desc文件,然后再来解析desc文件。

编译的话,就用Java调用一个cmd指令就好了

解析的话,就有不少坑了:

第1坑:List

里的int需要额外处理

第2坑:map字段需要额外处理

第3坑:message嵌套message需要额外处理

第4坑:类名重复问题,因为我之前都是直接生成到一个包里面的,这就难免重复了,所以protobuf里面定义的package字段也要用上,

其它的小坑就略过不提了~~~

如何生成Java代码?

这肯定要用到模板引擎来做代码自动生成了,这里我使用的是FreeMaker。因为之前没玩过它,在写ftl模板文件时,经常运行失败。后来才知道这个坑:FreeMaker的数据模型必须有get方法,哪怕你的字段是public的也必须要有。

了解了FreeMaker的语法之后,再来写的话,就方便多了,后来而遇到了一个坑:Class里面嵌套Class的问题。这种内部类该怎么生成?这就需要用到FreeMaker的宏来做递归了。

2. 案例

ProtoBuf定义

文件名:user.proto,这里仅是一个示例,其实这个定义文件也不规范,更好的定义规范,还需要实践探索,和自己的项目结合

syntax = "proto3";

package crm.usercenter;

message UserMessage {

string name = 1;

int32 id = 2;

string message = 3;

MessageType type=4;

}

message FindUserMessageByIdReq {

int32 id = 1;

}

message Result {

string msg = 4;

int32 code = 5;

}

enum MessageType {

SYSTEM = 0;

CUSTOMER = 1;

OTHER = 2;

}

service UserPublic {

rpc FindUserMessageById (FindUserMessageByIdReq) returns (UserMessage);

rpc AddUserMessage (UserMessage) returns (Result);

}

生成的实体类

这里就只贴一个message了,其它类似

package dest.bean.crm.usercenter;

import dest.bean.crm.usercenter.MessageType;

public class UserMessage{

public String name;

public int id;

public String message;

public MessageType type;

}

生成的枚举类

package dest.bean.crm.usercenter;

public enum MessageType {

SYSTEM,

CUSTOMER,

OTHER,

}

生成的接口类

package dest.bean.crm.usercenter;

import dest.bean.crm.usercenter.FindUserMessageByIdReq;

import dest.bean.crm.usercenter.UserMessage;

import dest.bean.crm.usercenter.Result;

public interface UserPublic{

UserMessage findUserMessageById(FindUserMessageByIdReq findUserMessageByIdReq);

Result addUserMessage(UserMessage userMessage);

}

3. 异步封装

上面只是最基本的封装而已,其实用处不是很大,存在问题:

实体类自动生成,虽然减轻了一点工作量,不用自己再写那么多字段了,但是和Grpc生产的实体类之间的转换还是问题

生成的接口是同步的,但是我们在Android端用的话,肯定是要异步的接口的

下面,好戏开始上演了~~~

生成的异步接口

这里的异步接口,涉及到了我自己封装的网络层框架的几个接口了,

ResourceObserver是基于观察者模式实现的,类似于一个Rx的Disposable,用于取消网络请求,需要注册到ResourceSubject上。我们最好是将BaseActivity和BaseFragment都实现ResourceSubject接口,然后在退出Activity就可以自动取消网络请求了。

然后Consumer是我自己封装的函数式接口,之所以不用Rx和Java8的,主要是这种接口定义本来很简单,没必要过度依赖第三方,万一哪一天,我们不想用Rx了的话,用到这个接口的地方都得改,很蛋疼的。类似的我还封装了Mapper、Provider等几个函数式接口。

package dest.bean.crm.usercenter;

import com.ezbuy.functions.Consumer;

import com.ezbuy.web.ResourceObserver;

import dest.bean.crm.usercenter.FindUserMessageByIdReq;

import dest.bean.crm.usercenter.UserMessage;

import dest.bean.crm.usercenter.Result;

public interface UserPublicWebService{

ResourceObserver findUserMessageById(FindUserMessageByIdReq findUserMessageByIdReq, Consumer consume

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值