You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Native Image无法序列化Protobuf对象求助

问题描述

我有如下方法:

@Cacheable(value = USER_IDENTITIES_PROTO_CACHE)
public UserIdentitiesResponseOuterClass.UserIdentitiesResponse findUserIdentitiesProto(UUID userId) {

其中UserIdentitiesResponseOuterClass.UserIdentitiesResponse是Protobuf生成的类。JVM模式下运行一切正常,但在Native Image环境中抛出异常:

"Failure putting into cache: user_identities_proto. Cannot serialize"

我尝试添加了以下Runtime Hints代码,但问题并未解决:

hints.serialization()
        .registerType(TypeReference.of(UserIdentitiesResponseOuterClass.class))
        .registerType(UserIdentitiesResponseOuterClass.UserIdentity.class)
        .registerType(UserIdentitiesResponseOuterClass.UserIdentitiesResponse.class);

我使用Redis作为缓存,序列化方式为JdkSerializationRedisSerializer。

Proto文件内容如下:

syntax = "proto3";

package identity;

message UserIdentity {
  string provider = 1;
  string sub = 2;
}

message UserIdentitiesResponse {
  UserIdentity primary = 1;
  repeated UserIdentity identities = 2;
}

JVM模式下Protobuf对象的序列化/反序列化都正常,但Native Image模式下就出现上述序列化失败的问题。我认为应该补充Runtime Hints,但已经尝试了上面的代码,不清楚还缺什么,求解决办法?

解决方案

1. 补全Protobuf类的序列化相关Runtime Hints

JDK序列化依赖类的无参构造器、writeObject/readObject等序列化方法,但Native Image默认不会自动保留这些内容,哪怕Protobuf生成的类自带这些逻辑。你需要额外注册以下内容:

  • 每个Protobuf生成类的无参构造器
  • 序列化过程中可能用到的Builder内部类
  • repeated字段对应的数组类型(比如UserIdentity[])

修改Runtime Hints代码如下:

hints.serialization()
        // 注册主响应类
        .registerType(UserIdentitiesResponseOuterClass.UserIdentitiesResponse.class)
        // 注册嵌套的UserIdentity类
        .registerType(UserIdentitiesResponseOuterClass.UserIdentity.class)
        // 注册repeated字段对应的数组类型
        .registerType(UserIdentitiesResponseOuterClass.UserIdentity[].class)
        // 注册Builder类,JDK序列化过程中可能会用到
        .registerType(UserIdentitiesResponseOuterClass.UserIdentitiesResponse.Builder.class)
        .registerType(UserIdentitiesResponseOuterClass.UserIdentity.Builder.class);

// 单独注册无参构造器,确保Native Image能调用到
hints.reflection()
        .registerConstructor(UserIdentitiesResponseOuterClass.UserIdentitiesResponse.class.getDeclaredConstructor(), ExecutableMode.INVOKE)
        .registerConstructor(UserIdentitiesResponseOuterClass.UserIdentity.class.getDeclaredConstructor(), ExecutableMode.INVOKE);

2. 换用Protobuf专用序列化器(更推荐)

JdkSerializationRedisSerializer本身效率低,对Native Image的兼容性也差,不如直接换用Protobuf专用的序列化方式。比如基于Protobuf原生API自定义序列化器:

示例:自定义Protobuf Redis序列化器

@Bean
public RedisSerializer<Object> protobufRedisSerializer() {
    return new RedisSerializer<>() {
        @Override
        public byte[] serialize(Object o) throws SerializationException {
            if (o instanceof Message) {
                return ((Message) o).toByteArray();
            }
            throw new SerializationException("只支持Protobuf Message类型的对象");
        }

        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            return UserIdentitiesResponseOuterClass.UserIdentitiesResponse.parseFrom(bytes);
        }
    };
}

然后给RedisTemplate配置这个序列化器:

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory, RedisSerializer<Object> protobufRedisSerializer) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(connectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(protobufRedisSerializer);
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(protobufRedisSerializer);
    return template;
}

3. 手动添加反射配置文件

如果坚持使用JDK序列化,可以在项目的src/main/resources/META-INF/native-image目录下添加reflect-config.json文件,手动指定要保留的Protobuf类的反射信息:

[
  {
    "name": "identity.UserIdentitiesResponseOuterClass$UserIdentitiesResponse",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  },
  {
    "name": "identity.UserIdentitiesResponseOuterClass$UserIdentity",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  },
  {
    "name": "identity.UserIdentitiesResponseOuterClass$UserIdentity[]"
  }
]

内容的提问来源于stack exchange,提问作者Денис Анд

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 05:32:48