PB热部署—动态获取到最新的PB结构(DynamicMessage)

诉求

  1. 如何自动的获取到proto文件的message的结构。
  2. 或者根据最新包动态的解析成最新的JSON串。
  3. 在服务器收到客户端发来的消息进行反序列化时,根据 消息 msgId ,对消息体进行解析到对应的消息。
流程图.png

前置知识:
【平台化引擎】根据maven坐标—获取到jar的Class文件(URLClassLoader)
Protobuf | 如何在 MAC 上安装 Protobuf 编译 proto 文件
Java使用ProtoBuffer3时报错: Cannot resolve method 'isStringEmpty' in 'GeneratedMessageV3'

原理

protobuf对于每个元素都有一个相应的descriptor,这个descriptor包含该元素的所有元信息,非常类似于Spring中的Bean Definition。下面是各个Descriptor(元数据描述类)的类图:

descriptor的UML.png
  1. FileDescriptor: 对一个proto文件的描述,它包含文件名、包名、选项(如package, java_package, java_outer_classname等)、文件中定义的所有message、文件中定义的所有enum、文件中定义的所有service、文件中所有定义的extension、文件中定义的所有依赖文件(import)等。在FileDescriptor中还存在一个DescriptorPool实例,它保存了所有的dependencies(依赖文件的FileDescriptor)、name到GenericDescriptor的映射、字段到FieldDescriptor的映射、枚举项到EnumValueDescriptor的映射,从而可以从该DescriptorPool中查找相关的信息,因而可以通过名字从FileDescriptor中查找Message、Enum、Service、Extensions等。可以通过--descriptor_set_out指定生成某个proto文件相对应的FileDescriptorSet文件。
  2. Descriptor: 对一个message定义的描述,它包含该message定义的名字、所有字段、内嵌message、内嵌enum、关联的FileDescriptor等。可以使用字段名或字段号查找FieldDescriptor。
  3. FieldDescriptor:对一个字段或扩展字段定义的描述,它包含字段名、字段号、字段类型、字段定义(required/optional/repeated/packed)、默认值、是否是扩展字段以及和它关联的Descriptor/FileDescriptor等。
  4. EnumDescriptor:对一个enum定义的描述,它包含enum名、全名、和它关联的FileDescriptor。可以使用枚举项或枚举值查找EnumValueDescriptor。
  5. EnumValueDescriptor:对一个枚举项定义的描述,它包含枚举名、枚举值、关联的EnumDescriptor/FileDescriptor等。
  6. ServiceDescriptor:对一个service定义的描述,它包含service名、全名、关联的FileDescriptor等。
  7. MethodDescriptor:对一个在service中的method的描述,它包含method名、全名、参数类型、返回类型、关联的FileDescriptor/ServiceDescriptor等。

实现

1.1 定义pb文件

syntax = "proto3";
option java_package = "com.tellme.cinema";

message UserInfo{
  int64 user_id = 1;
  string user_name = 2;
}
syntax = "proto3";
import "base.proto";
option java_package = "com.tellme.cinema";

enum MovieType{
  CHILDREN = 0;
  ADULT = 1;
  NORMAL = 2;
  OHTER = 3;
}
enum Gender{
  MAN = 0;
  WOMAN = 1;
  OTHER = 2;
}

message Movie{
  string name = 1;
  MovieType type = 2;
  int32 releaseTimeStamp = 3;
  string description = 4;
  string address = 5;
  UserInfo user_info = 6;
}

message Resp{
  int32 code = 1;
  string message = 2;
}


message Customer{
  string name = 1;
  Gender gender = 2;
  int32 birthdayTimeStamp = 3;
}

message Ticket{
  int32 id = 1;
  repeated Movie movie = 2;
  Customer customer = 3;
}

service TestService {
  rpc test(Movie) returns (Resp);
}
syntax = "proto3";
option java_package = "com.tellme.cinema";

message PbInfo{
  string root_file_descriptor_name = 1;
  string root_message_type_name = 2;
  bytes file_descriptor_set = 3;
}

编译proto文件:

protoc --java_out=./tt-component/src/main/java -I=./tt-component/src/main/proto pbInfo.proto cinema1.proto base.proto

1.2 Message使用

(1)简单使用

    public static void main(String[] args) throws Exception {
        Cinema1.Movie movie = Cinema1.Movie.newBuilder().setAddress("北京").setName("tom")
                .setUserInfo(Base.UserInfo.newBuilder().setUserId(1001L).build()).build();
        //Message的数组
        byte[] bytes = movie.toByteArray();

        //知道Message类名
        String className = "com.tellme.cinema.Cinema1$Movie";
        Class<?> clazz = Class.forName(className);
        Method method = clazz.getMethod("getDescriptor");
        Descriptors.Descriptor descriptor = (Descriptors.Descriptor) method.invoke(null);

        DynamicMessage dynamicMessage2 = DynamicMessage.newBuilder(descriptor).mergeFrom(bytes).build();
        String data2 = printer().includingDefaultValueFields()
                .preservingProtoFieldNames()
                .printingEnumsAsInts()
                .print(dynamicMessage2);
        System.out.println(data2);
    }

(2)复杂使用

传入proto的文件名,然后找到对应的Message对象

private static void test() throws Exception {
        Cinema1.Movie movie = Cinema1.Movie.newBuilder().setAddress("北京").setName("tom")
                .setUserInfo(Base.UserInfo.newBuilder().setUserId(1001L).build()).build();
        //Message的数组
        byte[] bytes = movie.toByteArray();
        //知道proto类名
        String className = "com.tellme.cinema.Cinema1";
        Class<?> clazz = Class.forName(className);
        //获取到pb文件的描述符
        Method method = clazz.getMethod("getDescriptor");
        Descriptors.FileDescriptor fileDescriptor = (Descriptors.FileDescriptor) method.invoke(null);
        //获取到扩展字段
        List<Descriptors.Descriptor> messageTypes = fileDescriptor.getMessageTypes();
        for (Descriptors.Descriptor descriptor : messageTypes) {
            System.out.println(descriptor.getName());
            //获取到描述结构
            String jsonString = toJsonString(descriptor);
            System.out.println(jsonString);
            //获取到文件描述符
            if ("Movie".equals(descriptor.getName())) {
                DynamicMessage dynamicMessage = DynamicMessage.newBuilder(descriptor).mergeFrom(bytes).build();
                String data1 = printer().includingDefaultValueFields()
                        .preservingProtoFieldNames()
                        .printingEnumsAsInts()
                        .print(dynamicMessage);
                System.out.println(data1);
            }
        }
    }

(3)解析出来的descriptor对象如何跨服务传输

工具类:

public class DescriptorUtil {

    private static String getRootMessageTypeName(String protoClassName) {
        if (protoClassName.contains("$")) {
            return protoClassName.substring(protoClassName.lastIndexOf("$") + 1);
        } else {
            return protoClassName.substring(protoClassName.lastIndexOf(".") + 1);
        }
    }
    /**
     * 重新构建descriptor对象。
     * @param rootFileDescriptorName cinema1.proto
     * @param rootMessageTypeName Movie
     * @param descData 传入的proto的描述+依赖项的描述
     * @return
     * @throws Exception
     */
    public static Descriptors.Descriptor buildDescriptor(String rootFileDescriptorName, String rootMessageTypeName, byte[] descData)
            throws Exception {
        System.out.println("rootFileDescriptorName:"+rootFileDescriptorName);
        System.out.println("rootMessageTypeName:"+rootMessageTypeName);
        //文件依赖关系
        DescriptorProtos.FileDescriptorSet fileDescriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descData);
        Map<String, Descriptors.FileDescriptor> fdMap = new HashMap<>();

        Map<String, DescriptorProtos.FileDescriptorProto> name2FileDescriptorProtoMap =
                fileDescriptorSet.getFileList().stream().collect(Collectors.toMap(DescriptorProtos.FileDescriptorProto::getName, Function.identity()));

        DescriptorProtos.FileDescriptorProto rootFileDescProto = name2FileDescriptorProtoMap.get(rootFileDescriptorName);
        Descriptors.FileDescriptor rootFileDescriptor =
                Descriptors.FileDescriptor.buildFrom(rootFileDescProto, getDependencies(name2FileDescriptorProtoMap, fdMap, rootFileDescProto));
        return rootFileDescriptor.findMessageTypeByName(rootMessageTypeName);
    }

    /**
     * 重建依赖
     */
    private static Descriptors.FileDescriptor[] getDependencies(
            Map<String, DescriptorProtos.FileDescriptorProto> name2FileDescriptorProtoMap, Map<String, Descriptors.FileDescriptor> fdMap,
            DescriptorProtos.FileDescriptorProto fileDescProto) throws Exception {
        if (fileDescProto.getDependencyCount() == 0) {
            return new Descriptors.FileDescriptor[0];
        }
        String[] dependencyArray = fileDescProto.getDependencyList().toArray(new String[0]);
        int length = dependencyArray.length;
        Descriptors.FileDescriptor[] dependencies = new Descriptors.FileDescriptor[length];
        for (int i = 0; i < length; i++) {
            DescriptorProtos.FileDescriptorProto dependencyFileDescProto = name2FileDescriptorProtoMap.get(dependencyArray[i]);
            Descriptors.FileDescriptor fd = fdMap.get(dependencyArray[i]);
            if (fd != null) {
                dependencies[i] = fd;
            } else {
                Descriptors.FileDescriptor dependencyFileDesc = Descriptors.FileDescriptor.buildFrom(
                        dependencyFileDescProto, getDependencies(name2FileDescriptorProtoMap, fdMap, dependencyFileDescProto));
                dependencies[i] = dependencyFileDesc;
                fdMap.put(dependencyArray[i], dependencyFileDesc);
            }
        }
        return dependencies;
    }

    /**
     * 获取文件描述符列表(包括自身+依赖项)
     */
    public static DescriptorProtos.FileDescriptorSet getFileDescriptorSetForDependency(Descriptors.FileDescriptor fileDescriptor) {
        Set<Descriptors.FileDescriptor> dependencies = getAllPbDependencies(fileDescriptor);
        DescriptorProtos.FileDescriptorSet.Builder fileDescriptorSetBuilder = DescriptorProtos.FileDescriptorSet.newBuilder();
        fileDescriptorSetBuilder.addFile(fileDescriptor.toProto());
        for (Descriptors.FileDescriptor dependency : dependencies) {
            fileDescriptorSetBuilder.addFile(dependency.toProto());
        }
        return fileDescriptorSetBuilder.build();
    }

    private static Set<Descriptors.FileDescriptor> getAllPbDependencies(Descriptors.FileDescriptor fileDescriptor) {
        Set<Descriptors.FileDescriptor> result = new HashSet<>();
        List<Descriptors.FileDescriptor> dependencies = fileDescriptor.getDependencies();
        for (Descriptors.FileDescriptor dependency : dependencies) {
            result.add(dependency);
            result.addAll(getAllPbDependencies(dependency));
        }
        return result;
    }
}

使用:

  private static void test1() throws Exception {
        Cinema1.Movie movie = Cinema1.Movie.newBuilder().setAddress("北京").setName("tom")
                .setUserInfo(Base.UserInfo.newBuilder().setUserId(1001L).build()).build();
        //Message的数组
        byte[] bytes = movie.toByteArray();
        //拿到了对象名
        String className = "com.tellme.cinema.Cinema1$Movie";
        Class<?> clazz = Class.forName(className);
        Method method = clazz.getMethod("getDescriptor");
        Descriptors.Descriptor d1 = (Descriptors.Descriptor) method.invoke(null);
        //获取到Descriptor配置
        Descriptors.FileDescriptor fileDescriptor = d1.getFile();
        //类似于远程调用,一个服务解析出pb结构,传递给另一个服务;
        PbInfoOuterClass.PbInfo pbInfo = getPbInfoForMock(className, fileDescriptor);
        Descriptors.Descriptor descriptor = buildDescriptor(pbInfo.getRootFileDescriptorName(), pbInfo.getRootMessageTypeName(), pbInfo.getFileDescriptorSet().toByteArray());
        DynamicMessage dynamicMessage = DynamicMessage.newBuilder(descriptor).mergeFrom(bytes).build();
        String data1 = printer().includingDefaultValueFields()
                .preservingProtoFieldNames()
                .printingEnumsAsInts()
                .print(dynamicMessage);
        //得到JSON的目标对象,就可以反序列化成map参与后续逻辑
        System.out.println(data1);
    }

1.3 Method使用

(1)简单使用

private static void test2() throws Exception {
        String className = "com.tellme.cinema.Cinema1";
        Class<?> clazz = Class.forName(className);
        //获取到pb文件的描述符
        Method method = clazz.getMethod("getDescriptor");
        Descriptors.FileDescriptor fileDescriptor = (Descriptors.FileDescriptor) method.invoke(null);
        List<Descriptors.ServiceDescriptor> services = fileDescriptor.getServices();
        for (Descriptors.ServiceDescriptor serviceDescriptor : services) {
            Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName("test");
            printPrzm(methodDescriptor);
        }
    }

    private static void printPrzm(Descriptors.MethodDescriptor methodDescriptor) {
        //输入参数
        Descriptors.Descriptor inputType = methodDescriptor.getInputType();
        Map<String, Object> param = new HashMap<>();
        param.put("address", "北京");
        param.put("name", "tom");
        HashMap<String, Object> userInfo = Maps.newHashMap();
        userInfo.put("userId", 1001);
        userInfo.put("userName", "tom");
        param.put("userInfo", userInfo);

        String jsonString = JSON.toJSONString(param);
        System.out.println(jsonString);
        //二进制Message
        ByteString byteString = MetaInfoUtil.toByteString(inputType, jsonString);

        String jsonString1 = toJsonString(inputType, byteString);
        System.out.println(jsonString1);
    }

(2)进阶使用

    private static void test4() throws Exception {
        //拿到了对象名
        String className = "com.tellme.cinema.Cinema1";
        Class<?> clazz = Class.forName(className);
        Method method = clazz.getMethod("getDescriptor");
        Descriptors.FileDescriptor fileDescriptor = (Descriptors.FileDescriptor) method.invoke(null);
        //将文件格式与依赖格式扁平化,
        DescriptorProtos.FileDescriptorSet descriptorSet = getFileDescriptorSetForDependency(fileDescriptor);
        Descriptors.MethodDescriptor methodDescriptor = getMethodDescriptor("TestService/test", Lists.newArrayList(descriptorSet));
        System.out.println(methodDescriptor);
        printPrzm(methodDescriptor);
    }

1.4 依赖工具类

@Slf4j
public class MetaInfoUtil {

    public static Descriptors.MethodDescriptor getMethodDescriptor(String fullMethodName, List<DescriptorProtos.FileDescriptorSet> fileDescriptorSets) {
        Preconditions.checkArgument(StringUtils.isNotEmpty(fullMethodName), "fullMethodName 不能为空");
        List<DescriptorWrapper> sericeWrapperList =
                fileDescriptorSets.stream().map(DescriptorWrapper::fromFileDescriptorSet).collect(Collectors.toList());
        for (DescriptorWrapper wrapper : sericeWrapperList) {
            //根据方法名,查询方法出入参。
            Descriptors.MethodDescriptor methodDescriptor = wrapper.findMethodDescriptor(fullMethodName);
            if (methodDescriptor != null) {
                return methodDescriptor;
            }
        }
        return null;
    }

    /**
     * 转化为Json串
     */
    public static String toJsonString(Descriptors.Descriptor descriptor, ByteString object) {
        try {
            DynamicMessage message = DynamicMessage.newBuilder(descriptor).mergeFrom(object).build();
            return printer()
                    .includingDefaultValueFields()
                    .preservingProtoFieldNames()
                    .printingEnumsAsInts()
                    .print(message);

        } catch (InvalidProtocolBufferException e) {
            log.error(e.getMessage());
        }
        return null;
    }


    /**
     * 转化为Message的二进制流
     */
    public static ByteString toByteString(Descriptors.Descriptor descriptor, String json) {
        JsonFormat.Parser parser = JsonFormat.parser()
                .usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder()
                        .add(descriptor)
                        .build())
                .ignoringUnknownFields();
        DynamicMessage.Builder messageBuilder = DynamicMessage.newBuilder(descriptor);
        try {
            parser.merge(json, messageBuilder);
            return messageBuilder.build().toByteString();
        } catch (InvalidProtocolBufferException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    //打印出Message的结构(JSON化)
    public static String toJsonString(Descriptors.Descriptor descriptor) {
        DynamicMessage.Builder builder = null;
        try {
            builder = DynamicMessage.newBuilder(descriptor);
            JsonObject jsonObject = convertWithAllFields(builder, Sets.newHashSet());
            return jsonObject.toString();
        } catch (Throwable e) {
            log.error("递归获取json失败,可能会降级只获取json第一级的元素", e);
        }

        if (builder != null) {
            try {
                return printer()
                        .includingDefaultValueFields()
                        .preservingProtoFieldNames()
                        .printingEnumsAsInts()
                        .print(builder);
            } catch (InvalidProtocolBufferException e) {
                log.error("降级只获取json第一级的元素失败,返回", e);
            }
        }
        return null;
    }

    private static JsonObject convertWithAllFields(DynamicMessage.Builder builder, Set<String> existField) throws Exception {
        Descriptors.Descriptor descriptor = builder.getDescriptorForType();
        String jsonString = printer().includingDefaultValueFields().preservingProtoFieldNames().printingEnumsAsInts()
                .print(builder);
        JsonObject json = new JsonParser().parse(jsonString).getAsJsonObject();
        for (Descriptors.FieldDescriptor field : descriptor.getFields()) {
            if (field.toProto().getTypeName().equals(".google.protobuf.Any") || !field.getJavaType().equals(MESSAGE)) {
                continue; // any类型,什么也不做;非MESSAGE类型,什么也不做
            }
            if (field.isRepeated()) {
                convertRepeatedField(field, builder, existField, json);
            } else {
                convertNotRepeatedField(field, builder, existField, descriptor, json);
            }
        }
        return json;
    }

    private static void convertRepeatedField(Descriptors.FieldDescriptor field, DynamicMessage.Builder builder,
                                             Set<String> exist, JsonObject json) throws Exception {
        // 是repeated类型且不是map类型(pb定义map不允许是repeated的,但是这里不知道为什么拿到的map也是repeated的,所以过滤下map类型)
        if (!field.isMapField()) {
            json.add(field.toProto().getName(), convertNotMapField(field, builder, exist));
            return;
        }
        // 如果是map,返回{"key":{}}形式
        exist.add(field.getMessageType().getFullName());
        Descriptors.Descriptor messageType = field.getMessageType();
        Descriptors.FieldDescriptor valueField = messageType.getFields().get(1);
        Descriptors.FieldDescriptor keyField = messageType.getFields().get(0);
        String key = "";
        switch (keyField.getJavaType()) {
            case INT:
            case LONG:
                key = "0";
                break;
            case BOOLEAN:
                key = "false";
                break;
            default:
                key = "";
        }
        if (valueField.getJavaType().equals(MESSAGE)) {
            JsonObject mapObject = new JsonObject();
            mapObject.add(key, new JsonObject());
            json.add(field.toProto().getName(), mapObject);
        } else {
            Object defaultValue = valueField.getDefaultValue();
            JsonObject jsonObject = new JsonObject();
            jsonObject.add(key, new Gson().toJsonTree(defaultValue));
            json.add(field.toProto().getName(), jsonObject);
        }

    }

    private static void convertNotRepeatedField(Descriptors.FieldDescriptor field, DynamicMessage.Builder builder, Set<String> exist,
                                                Descriptors.Descriptor descriptor, JsonObject json) throws Exception {
        if (field.toProto().hasOneofIndex()) {
            // oneof 只获取key, value需要用户自己补全
            descriptor.toProto().getOneofDeclList().forEach(proto -> json.add(proto.getName(), new JsonParser().parse("{}").getAsJsonObject()));
            return;
        }
        if (!exist.contains(field.getMessageType().getFullName())) {
            json.add(field.toProto().getName(), convertField(field, builder, exist));
        } else {
            exist.add(field.getMessageType().getFullName());
            json.add(field.toProto().getName(), new JsonParser().parse("{}").getAsJsonObject());
        }
    }

    private static JsonArray convertNotMapField(Descriptors.FieldDescriptor field, DynamicMessage.Builder builder,
                                                Set<String> exist) throws Exception {
        // 避免message里面包含自己导致的无限递归,如果已经解析过了就不再递归调用
        if (!exist.contains(field.getMessageType().getFullName())) {
            JsonArray array = new JsonArray(1);
            array.add(convertField(field, builder, exist));
            return array;
        }
        return new JsonArray(0);
    }

    private static JsonObject convertField(Descriptors.FieldDescriptor field, DynamicMessage.Builder builder, Set<String> exist) throws Exception {
        exist.add(field.getMessageType().getFullName());
        return convertWithAllFields(builder.newBuilderForField(field), exist);
    }
}

根据全类名+FileDescriptor获取到MethodDescriptor,以便反射调用。

@Slf4j
class DescriptorWrapper {

    private final ImmutableList<FileDescriptor> fileDescriptors;

    private DescriptorWrapper(Iterable<FileDescriptor> fileDescriptors) {
        this.fileDescriptors = ImmutableList.copyOf(fileDescriptors);
    }

    public static DescriptorWrapper empty() {
        return new DescriptorWrapper(Collections.emptyList());
    }

    static DescriptorWrapper fromFileDescriptorSet(DescriptorProtos.FileDescriptorSet descriptorSet) {
        ImmutableMap<String, FileDescriptorProto> descriptorProtoIndex =
                computeDescriptorProtoIndex(descriptorSet);
        Map<String, FileDescriptor> descriptorCache = new HashMap<>();

        ImmutableList.Builder<FileDescriptor> result = ImmutableList.builder();
        for (FileDescriptorProto descriptorProto : descriptorSet.getFileList()) {
            try {
                result.add(descriptorFromProto(descriptorProto, descriptorProtoIndex, descriptorCache));
            } catch (Descriptors.DescriptorValidationException e) {
                log.warn("Skipped descriptor " + descriptorProto.getName() + " due to error", e);
            }
        }
        return new DescriptorWrapper(result.build());
    }

    @Nullable
    Descriptors.MethodDescriptor findMethodDescriptor(String fullMethodName) {

        int methodIndex = StringUtils.lastIndexOf(fullMethodName, "/");
        String packageName;
        String serviceName;
        if (StringUtils.contains(fullMethodName, ".")) {
            int serviceIndex = StringUtils.lastIndexOf(fullMethodName, ".");
            packageName = StringUtils.substring(fullMethodName, 0, serviceIndex);
            serviceName = StringUtils.substring(fullMethodName, serviceIndex + 1, methodIndex);
        } else {
            packageName = "";
            serviceName = StringUtils.substring(fullMethodName, 0, methodIndex);
        }
        String methodName = StringUtils.substring(fullMethodName, methodIndex + 1);
        return findMethodDescriptor(packageName, serviceName, methodName);
    }

    private Descriptors.MethodDescriptor findMethodDescriptor(String packageName, String serviceName, String methodName) {
        ServiceDescriptor service = findServiceDescriptor(packageName, serviceName);
        return service != null ? service.findMethodByName(methodName) : null;
    }

    private ServiceDescriptor findServiceDescriptor(String packageName, String serviceName) {
        for (FileDescriptor fileDescriptor : fileDescriptors) {
            if (!fileDescriptor.getPackage().equals(packageName)) {
                continue;
            }

            ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
            if (serviceDescriptor != null) {
                return serviceDescriptor;
            }
        }
        return null;
    }

    private static ImmutableMap<String, FileDescriptorProto> computeDescriptorProtoIndex(
            DescriptorProtos.FileDescriptorSet fileDescriptorSet) {
        ImmutableMap.Builder<String, FileDescriptorProto> resultBuilder = ImmutableMap.builder();
        for (FileDescriptorProto descriptorProto : fileDescriptorSet.getFileList()) {
            resultBuilder.put(descriptorProto.getName(), descriptorProto);
        }
        return resultBuilder.build();
    }

    private static FileDescriptor descriptorFromProto(
            FileDescriptorProto descriptorProto,
            ImmutableMap<String, FileDescriptorProto> descriptorProtoIndex,
            Map<String, FileDescriptor> descriptorCache) throws Descriptors.DescriptorValidationException {
        // First, check the cache.
        String descriptorName = descriptorProto.getName();
        if (descriptorCache.containsKey(descriptorName)) {
            return descriptorCache.get(descriptorName);
        }

        // Then, fetch all the required dependencies recursively.
        ImmutableList.Builder<FileDescriptor> dependencies = ImmutableList.builder();
        for (String dependencyName : descriptorProto.getDependencyList()) {
            if (!descriptorProtoIndex.containsKey(dependencyName)) {
                throw new IllegalArgumentException("Could not find dependency: " + dependencyName);
            }
            FileDescriptorProto dependencyProto = descriptorProtoIndex.get(dependencyName);
            dependencies.add(descriptorFromProto(dependencyProto, descriptorProtoIndex, descriptorCache));
        }

        // Finally, construct the actual descriptor.
        FileDescriptor[] empty = new FileDescriptor[0];
        final FileDescriptor fileDescriptor =
                FileDescriptor.buildFrom(descriptorProto, dependencies.build().toArray(empty));
        descriptorCache.putIfAbsent(descriptorProto.getName(), fileDescriptor);
        return fileDescriptor;
    }
}

文章参考

protobuf中的反射
protobuf在java应用中通过反射动态创建对象(DynamicMessage)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,812评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,626评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,144评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,052评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,925评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,035评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,461评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,150评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,413评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,501评论 2 307
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,277评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,159评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,528评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,868评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,143评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,407评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,615评论 2 335

推荐阅读更多精彩内容