[BUG] JSONB Record反序列化setter方法匹配问题
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
*简要描述您碰到的问题。*
JSONB Record对象在反序列化时,setter方法匹配逻辑有问题
### 环境信息
*请填写以下信息:*
- OS信息: [e.g.:CentOS 8.4.2105 4Core 3.10GHz 16 GB] window 11
- JDK信息: [e.g.:Openjdk 1.8.0_312] jdk17
- 版本信息:[e.g.:Fastjson2 2.x.x] fastjson2.0.56
### 重现步骤
*如何操作可以重现该问题:*
1. 自定义的Record对象Money,其中包含了一些成员方法,类似add、sub、multiply、divide。示例代码:
```java
package com.xxx.boot.commons.domain.vo;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
public record Money(Long value) implements ValueObject, Comparable {
public static final String DEFAULT_STYLE = ",##0.00";
public static final Money ZERO = new Money(0L);
/**
* 单位:分
*/
public static Money of(Long value) {
Assert.notNull(value, "value");
return new Money(value);
}
public static Money ofYuan(BigDecimal decimal) {
Long value = Money.yuanToFen(decimal);
return CommonUtils.nonNull(value, Money::of);
}
/**
* 分 转 元(四舍五入)
*
* @return
* @author zhongl
*/
public BigDecimal toYuan() {
return fenToYuan(this.value);
}
public Money add(Money another) {
long value = this.value + null2Zero(another).value;
return Money.of(value);
}
public Money sub(Money another) {
long value = this.value - null2Zero(another).value;
return Money.of(value);
}
public Money multiply(Money another) {
long value = this.value * null2Zero(another).value;
return Money.of(value);
}
public Money divide(Money divisor) {
if (divisor == null || divisor.eq0()) {
throw new ArithmeticException("The divisor must not be null or zero.");
}
BigDecimal value = NumberUtils.divide(this.toYuan(), divisor.toYuan());
return Money.ofYuan(value);
}
public static Money null2Zero(Money money) {
return CommonUtils.defaultIfNull(money, Money.ZERO);
}
@Override
public int compareTo(Money anotherMoney) {
return compare(this.value, anotherMoney.value);
}
public static int compare(long x, long y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
```
2. 反序列化时,出现如下错误:
```java
Caused by: org.apache.dubbo.common.serialize.SerializationException: com.alibaba.fastjson2.JSONException: set divide error
... 148 more
Caused by: com.alibaba.fastjson2.JSONException: set divide error
at com.alibaba.fastjson2.reader.FieldReaderObject.accept(FieldReaderObject.java:281)
at com.alibaba.fastjson2.reader.ObjectReaderNoneDefaultConstructor.readJSONBObject(ObjectReaderNoneDefaultConstructor.java:173)
at com.alibaba.fastjson2.reader.ORG_6_6_ParkingChargeBillResult.readJSONBObject(Unknown Source)
at com.alibaba.fastjson2.JSONB.parseObject(JSONB.java:569)
at org.apache.dubbo.common.serialize.fastjson2.FastJson2ObjectInput.readObject(FastJson2ObjectInput.java:161)
at org.apache.dubbo.common.serialize.DefaultSerializationExceptionWrapper$ProxyObjectInput.readObject(DefaultSerializationExceptionWrapper.java:170)
... 145 more
Caused by: java.lang.reflect.InvocationTargetException
at jdk.internal.reflect.GeneratedMethodAccessor521.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at com.alibaba.fastjson2.reader.FieldReaderObject.accept(FieldReaderObject.java:276)
... 150 more
Caused by: java.lang.ArithmeticException: The divisor must not be null or zero.
at com.wwhlian.boot.commons.domain.vo.Money.divide(Money.java:75)
... 154 more
```
3. 经过定位发现,在调用`com.alibaba.fastjson2.util.BeanUtils.setters`方法,第564行时,这个判断逻辑直接把Money对象的divide作为了setter方法,导致divide在反序列化时被调用,从而导致如上错误。下面是源码中的处理逻辑:
```java
final int methodNameLength = methodName.length();
boolean nameMatch = methodNameLength > 3 && (methodName.startsWith("set") || returnType == objectClass);
```
### 期待的正确结果
*对您期望发生的结果进行清晰简洁的描述。*
Record在未指定反序列化方式的情况,也未精准匹配setter方法的时候,更好的选择是不是应该通过构造方法反序列化。
### 相关日志输出
*请复制并粘贴任何相关的日志输出。*
#### 附加信息
*如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。*
Contributor guide
Research direction
Start by inspecting BeanUtils.setters around line 564 and the record handling in ObjectReaderNoneDefaultConstructor.readJSONBObject. Trace why Money.divide is treated as a setter when JSONB deserializes the record, then verify that unmatched record properties use the constructor path without invoking methods such as divide.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100