美文网首页grpc微服务框架探索
ProtoBuf进阶-反射创建Message对象

ProtoBuf进阶-反射创建Message对象

作者: zizi192 | 来源:发表于2018-08-24 16:42 被阅读788次

在网络层使用ProtoBuf协议与后台通信时,但是在项目中,由于历史原因,前端调用处更多采用<key,value>这种键值对。故需要用ProtoBuf的反射来动态创建对象,屏蔽网络层协议不同,对接口调用代码的影响。

在我的上篇笔记ProtoBuf使用初体验-Android Studio配置及JSON互转中可知,Android推荐用protobuf-lite来编译使用proto文件。但这带来一个问题,最终生成的proto代码不再具有Descriptor等描述信息,导致无法进行反射。故最终使用protobuf-java工具来编译.proto文件。

Android Studio配置调整

在proto模块的build.gradle文件中更改配置

apply plugin: 'com.android.library'
apply plugin: 'com.google.protobuf'
...
android {
    ...

    sourceSets {
        main {
            java {
                srcDir 'src/main/java'
            }
            proto {
                srcDir 'src/main/proto'
                include '**/*.proto'
            }
        }
    }

}

dependencies {
   ...
    api 'com.google.protobuf:protobuf-java:3.6.0'
    implementation 'com.google.protobuf:protoc:3.6.0'
    api 'com.googlecode.protobuf-java-format:protobuf-java-format:1.2'
}


protobuf {
    protoc {
        // You still need protoc like in the non-Android case
        artifact = 'com.google.protobuf:protoc:3.6.0'
    }
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                // In most cases you don't need the full Java output
                // if you use the lite output.
                remove java
            }
            task.builtins {
                java{}
            }
        }
    }
}

反射创建对象

public static Message buildMessage(String msgClassName,  Map<String, String> fields){
        Class cl = null;
        try {
            cl = Class.forName(msgClassName);
            Method method = cl.getMethod("newBuilder");    // newBuilder 为静态变量,即使没有 message 的具体实例也可以 invoke!yes!
            Object obj = method.invoke(null, new Object[]{});
            Message.Builder msgBuilder = (Message.Builder)obj;       // 得到 builder
            return buildMessage(msgBuilder, fields);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static Message buildMessage(Message.Builder builder, Map<String, String> fields) {
        Descriptors.Descriptor descriptor = builder.getDescriptorForType();
        for (Map.Entry<String, String> entry : fields.entrySet()) {
            if (entry.getValue() == null) {
                continue;
            }
            Descriptors.FieldDescriptor field = getField(descriptor, entry.getKey());
            if (field == null){
                continue;
            }
//            if (entry.getValue() instanceof List<?>) {
//                List<Object> values = (List<Object>) entry.getValue();
//                for (Object value : values) {
//                    builder.addRepeatedField(field, buildValue(builder, field, value));
//                }
//
//            } else {
                builder.setField(field, buildValue(builder, field, entry.getValue()));
//            }
        }
        return builder.build();
    }

    @SuppressWarnings("unchecked")
    private static Object buildValue(
            Message.Builder parentBuilder, Descriptors.FieldDescriptor field, Object value) {
        if (field.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
            if (field.isRepeated()) {}
            Message.Builder fieldBuilder = parentBuilder.newBuilderForField(field);
            return buildMessage(fieldBuilder, (Map<String, String>) value);
        } else if (field.getType() == Descriptors.FieldDescriptor.Type.ENUM) {
            return field.getEnumType().findValueByName((String) value);
        } else {
            switch (field.getJavaType()) {
                case FLOAT: // float is a special case
                    return Float.valueOf(value.toString());
                case INT:
                    return Integer.valueOf(value.toString());
                case LONG:
                    return Long.valueOf(value.toString());
                case DOUBLE:
                    return Double.valueOf(value.toString());
                default:
                    return value;
            }
        }
    }

    private static Descriptors.FieldDescriptor getField(Descriptors.Descriptor descriptor, String name) {
        return descriptor.findFieldByName(name);
    }

数据类型

附上一张protobuf和java等数据结构类型的定义供参考

image.png

参考文档:
Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南
一种java对象转换成protobuf对象通用方法
protobuf和json互转时应该注意的问题
protobuf java 反射

https://www.programcreek.com/java-api-examples/?class=com.google.protobuf.Message.Builder&method=setField

https://www.programcreek.com/java-api-examples/?code=google/rejoiner/rejoiner-master/rejoiner/src/main/java/com/google/api/graphql/grpc/QueryResponseToProto.java#

相关文章

  • ProtoBuf进阶-反射创建Message对象

    在网络层使用ProtoBuf协议与后台通信时,但是在项目中,由于历史原因,前端调用处更多采用...

  • protobuf 与 Java 对象互转

    Java使用protobuf时,往往需要在Java对象(比如POJO)和protobuf message的Java...

  • 06-Java反射面试题(11题)

    1、除了使用new创建对象之外,还可以用什么方法创建对象? 使用Java反射可以创建对象! 2、Java反射创建对...

  • 反射(创建对象)

    拿着String类型的类名,想创建对象,就这样做

  • Android Handler源码之消息发送

    消息发送 创建消息 创建消息使用下面的代码: 创建消息,获取Message对象,最好的方法是调用Message.o...

  • 深入 ProtoBuf - 反射原理解析

    在介绍了 ProtoBuf 序列化原理之后,本文介绍 ProtoBuf 的反射技术原理。 反射技术简介 对于反射大...

  • Java 反射

    Java反射Java反射API获取Class对象通过反射创建实例对象,调用公共方法通过反射调用私有方法 一.Jav...

  • js进阶(二)

    第十二天 04-基础进阶-第02天{对象进阶、内置对象} 第十二天对象工厂模式创建对象构造函数模式创建对象原型模式...

  • 设计模式:单例模式(3)

    反射攻击解决方案及原理 测试反射是否可以创建单例对象 创建测试类 测试结果测试结果.png 结论通过反射可以创建对...

  • 反射机制

    反射机制用于创建类对象比较难的时候,但又必须去创建的对象 因此使用反射机制去创建类的详细信息 创建类的详细信息的方...

网友评论

    本文标题:ProtoBuf进阶-反射创建Message对象

    本文链接:https://www.haomeiwen.com/subject/taxiiftx.html