美文网首页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对象

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