Environment
- Dubbo version: 3.2.x / 3.3.x (all versions affected)
- Protocol: dubbo (hessian2) and triple (hessian2)
- JDK: 17
Steps to reproduce
- Define an RPC interface with
List<Byte> or Map<String, Byte> parameters:
public interface GreetingService {
String processByteCollection(List<Byte> byteList, Map<String, Byte> byteMap);
}
- Consumer sends
List<Byte> with values like (byte) 1, (byte) 2, (byte) 127
- Provider receives the parameters and checks element types
Expected behavior
Provider receives List<Byte> with elements of type java.lang.Byte.
Actual behavior
Provider receives List<Integer> — all Byte elements are deserialized as Integer. This causes ClassCastException when the provider code tries to use the elements as Byte.
Same issue affects List<Short>, List<Float>, Map<K, Byte>, Map<K, Short>, etc. — any collection/map with narrow number type arguments.
Root cause
Dubbo passes only the erased Class (e.g., List.class) to the serialization framework during request deserialization, discarding the generic Type (e.g., List<Byte>). The serialization framework (hessian2) has no way to know the element type should be Byte, so it defaults to Integer for small numbers.
Specifically:
ReflectionMethodDescriptor stores method.getParameterTypes() (raw Class[]) but not method.getGenericParameterTypes() (which includes ParameterizedType).
DecodeableRpcInvocation.drawArgs() only calls in.readObject(Class), never in.readObject(Class, Type).
ReflectionPackableMethod.WrapRequestUnpack similarly only passes Class<?>[] to MultipleSerialization.deserialize().
Hessian2ObjectInput.readObject(Class, Type) ignores the Type parameter entirely.
Tested & verified
- Reproduced with Dubbo
3.3.6 (official release) on both dubbo and triple protocol
- Fix verified with both dubbo and triple protocol via E2E testing
- Verified with Arthas that the new code paths are executed at runtime
Environment
Steps to reproduce
List<Byte>orMap<String, Byte>parameters:List<Byte>with values like(byte) 1, (byte) 2, (byte) 127Expected behavior
Provider receives
List<Byte>with elements of typejava.lang.Byte.Actual behavior
Provider receives
List<Integer>— allByteelements are deserialized asInteger. This causesClassCastExceptionwhen the provider code tries to use the elements asByte.Same issue affects
List<Short>,List<Float>,Map<K, Byte>,Map<K, Short>, etc. — any collection/map with narrow number type arguments.Root cause
Dubbo passes only the erased
Class(e.g.,List.class) to the serialization framework during request deserialization, discarding the genericType(e.g.,List<Byte>). The serialization framework (hessian2) has no way to know the element type should beByte, so it defaults toIntegerfor small numbers.Specifically:
ReflectionMethodDescriptorstoresmethod.getParameterTypes()(rawClass[]) but notmethod.getGenericParameterTypes()(which includesParameterizedType).DecodeableRpcInvocation.drawArgs()only callsin.readObject(Class), neverin.readObject(Class, Type).ReflectionPackableMethod.WrapRequestUnpacksimilarly only passesClass<?>[]toMultipleSerialization.deserialize().Hessian2ObjectInput.readObject(Class, Type)ignores theTypeparameter entirely.Tested & verified
3.3.6(official release) on both dubbo and triple protocol