微服务B读取Hazelcast缓存遇NoClassFoundException的解决办法
解决方案汇总
一、直接存储/读取字节数组(最稳妥,无内部API依赖)
方式1:手动序列化后存byte[]
在服务A中,将自定义类手动序列化为byte[]再存入Hazelcast,服务B直接读取byte[]即可,完全避开Hazelcast的自动反序列化逻辑:
- 服务A代码示例:
// 用Java原生序列化(也可替换为Kryo、Protostuff等高效序列化库) YourCustomClass obj = new YourCustomClass(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(obj); byte[] bytes = bos.toByteArray(); IMap<String, byte[]> map = hazelcastInstance.getMap("cache-name"); map.put("key", bytes);
- 服务B代码示例:
IMap<String, byte[]> map = hazelcastInstance.getMap("cache-name"); byte[] bytes = map.get("key"); // 若无需解析内容,直接使用bytes即可;若要解析需B有对应类定义(此场景不适用,故仅用于传输/存储字节数组)
方式2:利用Hazelcast自定义序列化器
通过自定义序列化器,强制Hazelcast将目标类序列化为byte[]存储,读取时直接返回byte[],不触发自动反序列化:
- 在服务A中实现序列化器,将自定义类转为
byte[]:
public class CustomClassSerializer implements StreamSerializer<byte[]> { @Override public int getTypeId() { return 1001; // 自定义唯一ID,需避免与Hazelcast内置ID冲突 } @Override public void write(ObjectDataOutput out, byte[] object) throws IOException { out.writeInt(object.length); out.write(object); } @Override public byte[] read(ObjectDataInput in) throws IOException { int length = in.readInt(); byte[] bytes = new byte[length]; in.readFully(bytes); return bytes; } @Override public void destroy() {} }
- 在A的Hazelcast配置中注册序列化器,指定对
YourCustomClass使用该序列化器:
Config config = new Config(); SerializationConfig serializationConfig = config.getSerializationConfig(); serializationConfig.addSerializerConfig(new SerializerConfig() .setTypeClass(YourCustomClass.class) .setImplementation(new CustomClassSerializer())); HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
- 服务B中同样注册该序列化器,读取时直接获取
byte[]:
IMap<String, byte[]> map = hazelcastInstance.getMap("cache-name"); byte[] bytes = map.get("key");
二、使用Hazelcast公开API获取原始序列化数据
利用Hazelcast的Data类型存储,这是Hazelcast内部用于存储序列化数据的类型,服务A将自定义类转为Data存入,服务B读取Data后直接提取字节数组:
- 服务A代码:
SerializationService serializationService = hazelcastInstance.getSerializationService(); YourCustomClass obj = new YourCustomClass(); Data data = serializationService.toData(obj); IMap<String, Data> map = hazelcastInstance.getMap("cache-name"); map.put("key", data);
- 服务B代码:
IMap<String, Data> map = hazelcastInstance.getMap("cache-name"); Data data = map.get("key"); byte[] bytes = data.toByteArray();
此方法依赖Hazelcast的SerializationService,属于半公开API,比反射更稳定。
三、反射调用内部方法(不推荐,仅应急)
你之前报错是因为getMethod只能获取public方法,而getInternal是protected且父类为包私有类,正确的反射调用方式如下:
IMap<String, Object> map = hazelcastInstance.getMap("cache-name"); // 强转为MapProxyImpl(需确保Hazelcast版本对应,内部类可能随版本变化) MapProxyImpl<String, Object> mapProxyImpl = (MapProxyImpl<String, Object>) map; // 获取父类MapProxySupport的Class对象 Class<?> mapProxySupportClass = mapProxyImpl.getClass().getSuperclass(); // 获取getInternal方法(用getDeclaredMethod而非getMethod) Method getInternalMethod = mapProxySupportClass.getDeclaredMethod("getInternal", Object.class); // 设置方法可访问 getInternalMethod.setAccessible(true); // 调用方法,返回的是Data对象 Data data = (Data) getInternalMethod.invoke(mapProxyImpl, "key"); // 提取字节数组 byte[] bytes = data.toByteArray();
⚠️ 注意:此方法依赖Hazelcast内部私有API,版本升级时极可能失效,生产环境禁止使用。
内容的提问来源于stack exchange,提问作者Curfew
相关产品推荐
相关产品推荐

