grpc微服务框架探索

ProtoBuf进阶-反射创建Message对象

2018-08-24  本文已影响788人  zizi192

在网络层使用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#

上一篇下一篇

猜你喜欢

热点阅读