lombok 的bug?lombok 导致 springmvc 使用 @RequestBody注解 接收 json数据 对象参数绑定失败

大家好,我是烤鸭:
    lombok 导致 springmvc 使用 @RequestBody注解 接收 json数据 对象参数绑定失败。
    环境版本:
        spring 5.x

1. 场景复现


    问题出现在创建对象的属性名称。比如我有一个类中的属性值是 
    String aTest;

    首字母小写,第二个字母大写。
    lombok 生成的get/set 方法是 getATest/setATest。
    而依据java的规范(用各种ide生成的get/set方法) 应该是 getaTest/setaTest。

2. 问题猜测

    lombok 生成的方法不符合 JavaBean get/set 规范,导致mvc 无法封装进入对象。
    尝试 form表单提交的方式 是没有问题的。
    问题在于json 的方式。或者说在于json序列化和反序列化时。
    spring json的包 默认使用的 com.fasterxml.jackson
    可以看一下 com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap
    的 _findWithAlias 方法,如图:

    BeanPropertyMap 获取到类中的属性是 atest,而前台传入的key是 aTest,无法匹配。
    将 前台传参改为

  {
        "atest":"1",
        "age":"2"
    }

  就可以匹配上了。

3.解决


    不使用lombok 或者 重写 get/set方法
    或者 不使用 com.fasterxml.jackson 序列化,使用 fastjson

    看下有人提出过issue,而作者也提出了解决办法。
    https://github.com/rzwitserloot/lombok/issues/1661
    

    

4.深入源码看一下


    首先分析一下 com.fasterxml.jackson。

    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver

    

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
		Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
		// ... 省略

		// 关键点部分 1 	 
		body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
				((HttpMessageConverter<T>) converter).read(targtestlass, msgToUse));

		// ... 省略
}			

后边调用的就是  BeanPropertyMap 的find 方法。

    再看一下 com.alibaba.fastjson
    通过修改 默认 json解析器。原来 AbstractJackson2HttpMessageConverter  改为  FastJsonHttpMessageConverter
    com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer
    的 deserialze 方法。
    可以看到 fastjson 将原来的属性 放到 fieldinfo 中。后续操作的的都是fieldinfo 对象。

  

5. 测试


前面写的还不够清晰,我们可以写个简单的test方法自己测一下。

ATest.java

package com.test.test.test;

import lombok.Data;

@Data
public class ATest{

    String aTest;

    String age;

}

TestJson.java

package com.test.test;

import com.alibaba.fastjson.JSONObject;
import com.test.controller.test.ATest;
import org.junit.Test;

import java.util.HashMap;


public class TestJson{

    @Test
    public void testJson() {
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("aTest","111");
        hashMap.put("age","111");
        ATest test = JSONObject.parseObject(JSONObject.toJSONString(hashMap), ATest.class);
        System.out.println("fastjson=================="+test);

        ATest o = (ATest) net.sf.json.JSONObject.toBean(net.sf.json.JSONObject.fromObject(hashMap), ATest.class);
        System.out.println("net.sf.json=================="+o);

    }
}

可以发现,fastjson 反序列化话的对象是正常的。
net.sf.json 是不行的。
net.sf.json 使用的是 org.apache.commons.beanutils.PropertyUtilsBean
getPropertyDescriptors 
beanInfo.getPropertyDescriptors();
可以看到获取到的 beanInfo 的 propertyName 是 ATest。

事实证明, lombok  这波操作确实有点问题,起码不兼容市面上大部分的json包。


总结:


最开始以为是mvc 无法封装 的问题,后来发现跟lombok有关(首字母小写、第二个字母大写时的get/set方法生成方式)
form表单提交没问题,发现跟json有关(序列化/反序列化)
再换了几个常见的json包。
fastjson 没问题,net.sf.json 和 jackson 都不行
lombok 这波操作就是逆规范的,没什么好说的。如果非要这种命名的话,建议重写get/set 方法。

发布了115 篇原创文章 · 获赞 58 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Angry_Mills/article/details/91947928