文章内部信息已脱敏。
有一次在测试环境调用网易电子签章平台的接口,用来生成印章图片。
首先用postman去测试接口,除了必传的固定请求头,请求体参数如下:
{
"userId": "***********",
"templateType": "STAR",
"color": "RED",
"hText": "合同专用章",
"qText": "2023-01-17"
}
返回的结果是印章图片的base64。转化为图片后是。
然后在java项目里去调用该接口,用了feign去调第三方平台接口。
但是发现返回的base64转化为图片后,始终是如下图,也就是传入的参数中hText(横向文)与qText(下旋文),这两个字段没有生效。
代码中的对象属性定义为:
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class EsignAddSealDTO {
private String userId;
private String templateType;
private String color;
private String hText;
private String qText;
}
仔细检查了代码,发现对象属性之类的代码并没有问题,那为什么这两个字段会没有生效呢?
由于postman接口可以返回正确的结果,并且java项目调用中也有返回,可以定位到还是传参对象出了问题。
EsignAddSealDTO req = EsignAddSealDTO.builder().userId("*************").templateType("STAR").color("RED")
.hText("合同专用章").qText("2023-01-17").build();
System.out.println(JSONObject.toJSONString(req));
打印传参对象的json字符串,发现结果为:
{"HText":"合同专用章","QText":"2023-01-17","color":"RED","templateType":"STAR","userId":"*************"}
!! 序列化的json库采用的是fastjson,序列化后的json字符串中hText和qText竟然属性名首字母变为了大写。怪不得这两个字段没有生效。
项目中的fastjson版本是2.0.9。
看一下原因,首先,使用了lombok的@Data注解之后,默认生成的hText属性的get方法名变为了getHText(), qText属性的get方法名变为了getQText()。
而在fastjson中,获取属性名的方法是com.alibaba.fastjson2.util.BeanUtils#getterName。
源码debug如下:
其中mthodName入参为“getQText”,namingStrategy传参为null, 方法中修改设值为“CamelCase”。
这边可以看到源码中,对于get开头的方法名,如getQText,首先取get后的字符串为chars, 这里chars就是[Q, T, e, x, t]。
chars首字母为Q, 这时候做判断,如果第二个字符为大写,则直接返回chars为属性名;如果第二个字符为小写,则将首字母变为小写,再返回。这里由于第二个字符为T,因此这里直接返回QText为属性名。
再举例:如果方法名是getUserId,取chars为[U, s, e, r, I, d], 取首字母为U,因为第二个字符为小写,因此将首字母变为小写,返回userId为属性名。
因此getQText方法得到的属性名就是QText。而不是类定义的属性名qText。
知道原因后,就简单了,可以不使用lombok,而是直接生成get/set方法,
public String gethText() {
return hText;
}
public void sethText(String hText) {
this.hText = hText;
}
public String getqText() {
return qText;
}
public void setqText(String qText) {
this.qText = qText;
}
就可以返回正确属性名了。
注意:
使用@Data注解,即使在属性hText、qText上加上@JsonProperty注解,依旧不起作用。但是加在其他属性上时生效的,如在color上加注解,可以修改序列化后的属性名。
@Data
@Builder
public class EsignAddSealDTO {
private String userId;
private String templateType;
@JsonProperty(value = "COLOR")
private String color;
@JsonProperty(value = "hText")
private String hText;
@JsonProperty(value = "qText")
private String qText;
}
生成的序列化json字符串为:
{"COLOR":"RED","HText":"合同专用章","QText":"2023-01-17","templateType":"STAR","userId":"*************"}
附录: 浏览源码顺序:
com.alibaba.fastjson.JSON#toJSONString(java.lang.Object)
com.alibaba.fastjson2.JSONWriter.Context#getObjectWriter(java.lang.reflect.Type,java.lang.Class)
com.alibaba.fastjson2.writer.ObjectWriterProvider#getObjectWriter(java.lang.reflect.Type,
java.lang.Class, boolean)
com.alibaba.fastjson2.writer.ObjectWriterCreatorASM#createObjectWriter
com.alibaba.fastjson2.util.BeanUtils#getterName