uuuyuqi commented on PR #16198:
URL: https://github.com/apache/dubbo/pull/16198#issuecomment-4219646137

   @oxsean Thanks for the review! Here is the complete reproduction demo:
   
   **Demo repo**: https://github.com/uuuyuqi/dubbo3-list-byte-bug-demo
   
   Clone, start Nacos, run `mvn spring-boot:run` for provider & consumer, then 
`curl http://localhost:8082/testByte`. With Dubbo 3.3.6, all `Byte` elements 
become `Integer` on the provider side.
   
   ### Root cause explained
   
   The issue is specific to **hessian2 serialization** with narrow number types 
(`Byte`, `Short`, `Float`) inside collections/maps. It is **not** about generic 
calls or `PojoUtils` — this happens with normal strongly-typed RPC calls.
   
   Here is why:
   
   1. **Hessian2 does not preserve the concrete type for small integers during 
serialization.** When hessian2 serializes a `Byte` value like `(byte) 1`, it 
writes it as a compact integer (single-byte encoding `0x91`). There is no type 
tag distinguishing `Byte` from `Integer` — it's just a number in the wire 
format. This is by design in the hessian2 protocol for compactness.
   
   2. **During deserialization, hessian2 reads this compact integer and 
produces `Integer` by default**, because it has no information about the target 
element type. The call chain is:
   
      ```
      DecodeableRpcInvocation.drawArgs()
        → in.readObject(List.class)          // only raw Class, no generic info
          → hessian2 deserializes the list
            → reads each element as Integer   // no way to know it should be 
Byte
      ```
   
   3. **The generic type information IS available** via 
`Method.getGenericParameterTypes()` on the provider side (local reflection, no 
wire transmission needed), but Dubbo never passes it to the serialization 
layer. The `ObjectInput.readObject(Class, Type)` API already exists in Dubbo's 
serialization interface but was never utilized in the deserialization path.
   
   This fix simply connects the existing dots:
   - `ReflectionMethodDescriptor` already calls `method.getParameterTypes()` → 
now also calls `method.getGenericParameterTypes()`
   - `DecodeableRpcInvocation.drawArgs()` already calls `in.readObject(Class)` 
→ now calls `in.readObject(Class, Type)` when generic info is available
   - `Hessian2ObjectInput.readObject(Class, Type)` already exists but ignored 
the `Type` param → now uses it to post-convert narrow number types
   
   No changes to the wire protocol, no impact on non-generic parameters (the 
`Type` equals the raw `Class` for non-parameterized types, so the original code 
path is used).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to