项目中实体类加lombok注解后,使用fastjson转JSONObject字段名首字母变大写

先来回顾一下场景:
项目中定义一个实体类,用于和数据库做对应,这里先贴上实体类的简化定义(只保留一个字段):

@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帮我们生成getset等方法。
然后呢,我们把数据库中的查询出来并映射到这个实体类之后,下一步操作就是返回到前端,代码如下:

  1. Controller层代码:
@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());
    }
}
  1. Service层代码:
@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将属性名称转换成字符串有两中策略:

  1. 用类属性的名称,比如用cSqh;
  2. 截取get方法后面的字符串,比如getCSqh()CSqh,并且如果发现截取后的字符串的第一个字母和第二个字母都是大写,就直接用,否则还会将第一个字符转换成小写,比如,如果截取后的字符串是Csqh,他就会转成csqh,但是如果截取得到的是CSqh,就会直接用CSqh;

具体的代码在com.alibaba.fastjson.util.TypeUtils#buildBeanInfo(java.lang.Class, java.util.Map, com.alibaba.fastjson.PropertyNamingStrategy, boolean)中:

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;而就在这个时候,我发现,项目中已经显示地给系统环境中定义过这样一个转换器:
项目中实体类加lombok注解后,使用fastjson转JSONObject字段名首字母变大写_第1张图片
而且没有设置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是一种特殊情况吧。

你可能感兴趣的:(Java)