Protobuf反射(java版)

前文(https://blog.csdn.net/liuxiao723846/article/details/96613960)介绍了如何使用动态的方式将消费者解放,生产者是否也能动态的解放呢?查阅了资料C++版本支持反射的方式构建消息(api的方式生成C++原文件),java版本对应的api没有找到,但是吧中间的过程记录一下。

先看下代码:

public static void generateMsg() {
		// 构造
		com.google.protobuf.DescriptorProtos.FileDescriptorProto.Builder fileDesProtoBuilder = FileDescriptorProto
				.newBuilder();
		fileDesProtoBuilder.setSyntax("proto3");
		fileDesProtoBuilder.setName("food.proto");// proto文件
		Builder fileOptionBuilder = FileOptions.newBuilder();
		fileOptionBuilder.setJavaPackage("com.lanjingling.pair");
		fileOptionBuilder.setJavaOuterClassname("Food");
		fileDesProtoBuilder.setOptions(fileOptionBuilder.build());

		com.google.protobuf.DescriptorProtos.DescriptorProto.Builder addMessageTypeBuilder = fileDesProtoBuilder
				.addMessageTypeBuilder();
		addMessageTypeBuilder.setName("Pair");// message name

		com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Builder fidldDesProtoBuilder = FieldDescriptorProto
				.newBuilder();
		fidldDesProtoBuilder.setName("key");
		fidldDesProtoBuilder.setTypeName("string");
		fidldDesProtoBuilder.setNumber(1);
		addMessageTypeBuilder.addField(fidldDesProtoBuilder.build());

		com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Builder fidldDesProtoBuilder1 = FieldDescriptorProto
				.newBuilder();
		fidldDesProtoBuilder1.setName("value");
		fidldDesProtoBuilder1.setTypeName("int32");
		fidldDesProtoBuilder1.setNumber(2);
		addMessageTypeBuilder.addField(fidldDesProtoBuilder1.build());

		FileDescriptorProto build = fileDesProtoBuilder.build();

		// 创建
		com.google.protobuf.DynamicMessage.Builder newBuilder = DynamicMessage
				.newBuilder(build);
		DynamicMessage build2 = newBuilder.build();
		System.out.println(build2);
		// TODO 上面的方式无法将消息生成对应的java类
		// 可以使用protoc命令生成java类,然后使用class load将类加载到jvm

		// 反射
		try {
			Class cl = Class.forName("com.lanjingling.pair.Food.$Pair");
			Method method = cl.getMethod("newBuilder");
			Object obj = method.invoke(null, new Object[] {});
			Message.Builder msgBuilder = (Message.Builder) obj;
			buildMessage(msgBuilder,new HashMap<>());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void buildMessage(Message.Builder msgBuilder,Map<String, String> map) {
		Descriptors.Descriptor descriptor = msgBuilder.getDescriptorForType();
		for (Map.Entry<String, String> entry : map.entrySet()) {
			Descriptors.FieldDescriptor filedDescriptor = descriptor.findFieldByName(entry.getKey());
			if (filedDescriptor == null) {
				continue;
			}
			boolean isRepeated = filedDescriptor.isRepeated();
			Descriptors.FieldDescriptor.JavaType type = filedDescriptor.getJavaType();
			if (isRepeated) {
				String value = entry.getValue();
				String[] strArray = value.split(",");
				for (int i = 0; i < strArray.length; ++i) {
					Object valueObject = getObject(strArray[i], type); // getObject
					if (valueObject == null) {
						continue;
					}
					msgBuilder.addRepeatedField(filedDescriptor, valueObject);
				}
			} else {
				Object valueObject = getObject(entry.getValue(), type);
				if (valueObject == null) {
					continue;
				}
				msgBuilder.setField(filedDescriptor,
						getObject(entry.getValue(), type));
			}
		}
		Message msg = msgBuilder.build();
	}

	private static Object getObject(String rawString,
			Descriptors.FieldDescriptor.JavaType type) {
		try {
			switch (type) {
			case INT:
				return Integer.valueOf(rawString);
			case LONG:
				return Long.valueOf(rawString);
			case FLOAT:
				return Float.valueOf(rawString);
			case DOUBLE:
				return Double.valueOf(rawString);
			case BOOLEAN:
				return Boolean.valueOf(rawString);
			case STRING:
				return rawString;
			default:
				// BYTE_STRING, ENUM, MESSAGE 哈哈先支持以上这些啦
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

代码流程:

1)利用FileDescriptorProto、FieldDescriptorProto等类,来构造一个proto消息;构造完打印结果如下:

name: "food.proto"
message_type {
  name: "Pair"
  field {
    name: "key"
    number: 1
    type_name: "string"
  }
  field {
    name: "value"
    number: 2
    type_name: "int32"
  }
}
options {
  java_package: "com.lanjingling.pair"
  java_outer_classname: "Food"
}
syntax: "proto3"

2)接下来,想找一个api将上面的proto消息动态编译,生成java源文件,然后加载类到jvm中。可惜java版本的protobuf没有找到这样的api(C++中有),那么这里只能是先将proto结构保存到文件中,然后用命令行的方式生成,然后加载了,代码如下:

String protoCMD="protoc --java_out=./ ./food.ptoro"
Process process = Runtime.getRuntime().exec(protocCMD);

//加载到jvm

3)通过java反射的方式加载对应的proto消息类,然后对其进行写入值。。。在写入值得时候,可以根据类型等进行判断

参考:

https://blog.csdn.net/wschli/article/details/49816139

https://www.jianshu.com/p/56fe36fe4ea9

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值