先来回顾一下场景:
项目中定义一个实体类,用于和数据库做对应,这里先贴上实体类的简化定义(只保留一个字段):
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ApiModel("申请基本信息表")
@Table(name = "db_dzsjdc.t_yw_sqjbxx")
public class TYwSqjbxx {
/**
* 主键UUID
*/
@Id
@Column(name = "c_sqh")
@ApiModelProperty("申请号")
private String cSqh;
/**
* 申请时间(存DT, 取D)
*/
@Column(name = "dt_sqsj")
@ApiModelProperty("申请时间(存DT, 取D)")
private Date dtSqsj;
说明: 这个实体类的定义就是常见操作,定义字段变量然后让lombok
帮我们生成get
、set
等方法。
然后呢,我们把数据库中的查询出来并映射到这个实体类之后,下一步操作就是返回到前端,代码如下:
@Controller
@RequestMapping("Sloved")
public class SlovedController {
/**
* html模板入口
*
* @return 'resources/templates'下的html模板
*/
@Autowired
private DcslService dcslservice;
@GetMapping()
public String index(HttpServletRequest request) {
return "dcsl/Sloved";
}
@ResponseBody
@RequestMapping("/rs1")
public ArteryPageableData<List<TYwSqjbxx>> rs1(IQueryInfo queryInfo) {
return QueryManager.datas(queryInfo, TYwSqjbxx.class, () -> dcslservice.SelectSloved());
}
}
@Service
public class DcslServiceImpl implements DcslService {
@Resource
private TYwSqjbxxDao sqjbxxDao;
private final DzsjdcCodeService dzsjdcCodeService;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Override
public List<TYwSqjbxx> SelectSloved() {
List<TYwSqjbxx> list = sqjbxxDao.SelcetSloved();
list.forEach(tYwSqjbxx -> tYwSqjbxx.setCSlzt(dzsjdcCodeService.getCodeName("1", tYwSqjbxx.getCSlzt())));
return list;
}
}
至此都是并没有什么“骚操作”,然后呢现象是前端的列表有些数据绑定失败,F12之后,我们发现获取到的数据是这样的:
{
"limit":10,
"offset":0,
"total":1,
"data":[
"CSqh":"测试申请号1",
"dtSqsj":"2019-12-10"
]
}
申请号字段,前端预期的是"cSqh",因为我们是按照实体类的名字来对应的,但是却变成了CSqh
,所以绑定失败,但是为什么呢?我们后台的所有相关代码都在上面了。
经过一段艰苦的debug,找到的原因是:在转换JSON的时候用的是FastJsonHttpMessageConverter
,其中fastjson将属性名称转换成字符串有两中策略:
getCSqh()
取CSqh
,并且如果发现截取后的字符串的第一个字母和第二个字母都是大写,就直接用,否则还会将第一个字符转换成小写,比如,如果截取后的字符串是Csqh
,他就会转成csqh
,但是如果截取得到的是CSqh
,就会直接用CSqh
;具体的代码在com.alibaba.fastjson.util.TypeUtils#buildBeanInfo(java.lang.Class>, java.util.Map
中:
List<FieldInfo> fieldInfoList = fieldBased ? computeGettersWithFieldBase(beanType, aliasMap, false,
propertyNamingStrategy) // : computeGetters(beanType, jsonType, aliasMap, fieldCacheMap,
false, propertyNamingStrategy);
从上面的代码可以看出,采用哪种策略,是依据fieldBased
变量的,而这个变量是SerializeConfig
的静态bool值,默认是false,所以默认用的是第二种策略,截取get方法的名称用作json的key;那这样就可以把锅甩给lombok了,因为lombok的@Data
,@Getter
注解,默认是将属性的首字母大写,然后拼在get后面,比如上面的例子,属性名叫cSqh
,lombok实际生成的class文件,反编译之后看到的get方法签名是getCSqh()
,这样fastjson和lombok各自的默认行为造成了如今的结果。
那解决方法就有了:
一. 自己写get、set方法,写成getcSqh()
,这里提一句,如果你用IDEA自动生成get/set方法,就是这样的,所以以前都没有遇到过这个问题;
二. 更改FastJsonHttpMessageConverter中SerializeConfig属性,将fieldBased
显示地设置成true
;而就在这个时候,我发现,项目中已经显示地给系统环境中定义过这样一个转换器:
而且没有设置fieldBased
,将fieldBased
显示地设置成true
;所以我们重写此类:
package com.thunisoft.artery.starter.config;
@Configuration
@ComponentScan({"com.thunisoft.artery.data", "com.thunisoft.artery.spring", "com.thunisoft.artery.init",
"com.thunisoft.artery.actuator", "com.thunisoft.artery.exportdata", "com.thunisoft.artery.component"})
public class ArteryStarterAutoConfigure implements WebMvcConfigurer{
@Autowired
private StringHttpMessageConverter stringHttpMessageConverter;
@Bean
@ConditionalOnMissingBean
@Qualifier("arteryCacheManager")
public ArteryCacheManager arteryCacheManager(CacheManager cacheManager) {
return new ArteryCacheManager(cacheManager);
}
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
WebMvcConfigurer.super.configureMessageConverters(converters);
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
fastJsonHttpMessageConverter.getFastJsonConfig().setSerializeConfig(new SerializeConfig(true));
// converters.add(0, fastJsonHttpMessageConverter);
converters.add(0, stringHttpMessageConverter);
}
}
至此该问题就解决了,总结一下吧,其实lombok、fastjson、Artery在这个问题里面各自没有做错什么,其实话说回来,我们也不应该这样定义变量的名字,如果我们直接写成sqh
也不会发生这个问题,外国人都是用英文的,不会有人定义变量的时候写STrCotent
这样的,驼峰式也会写成strContent
,所以fastjson可能觉得写成STrCotent
是一种特殊情况吧。