首先,我们先来看阿里巴巴的《Java开发手册》关于 isXXX 是怎么定义的吧:

【强制】POJO 类中布尔类型变量都不要加 is 前缀,否则部分框架解析会引起序列化错误。

反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),RPC 框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

就是说如果使用 isXXX 这种命名形式会引起潜在的异常。

我们来看一段由idea生成的getter/setter代码:

public class Student {
    private String name;
    private boolean isDeleted;

    public Student(String name, boolean isDeleted) {
        this.name = name;
        this.isDeleted = isDeleted;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isDeleted() {
        return isDeleted;
    }

    public void setDeleted(boolean deleted) {
        isDeleted = deleted;
    }
}

可以看到,isDeleted生成的getter/setter方法是isDeleted/setDeleted,这就会导致有些框架会去找对应的deleted变量,可是我们的变量名是isDeleted,并不是deleted。

经过验证,在fastjson和Jackson这两个序列化框架中,都会存在该问题。

测试fastjson序列化框架:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.9</version>
</dependency>
    @Test
    public void test1() throws JsonProcessingException {
        //fastjson 序列化框架
        Student student = new Student("aaa",true);
        String jsonString = JSON.toJSONString(student);
        System.out.println(jsonString);//输出{"deleted":true,"name":"aaa"}
    }

测试jackson序列化框架:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.13.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.13.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.4</version>
</dependency>
    @Test
    public void test1() throws JsonProcessingException {
        //jackson 序列化框架
        Student student = new Student("aaa",true);
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = objectMapper.writeValueAsString(student);
        System.out.println(jsonString);//输出{"deleted":true,"name":"aaa"}
    }

再提一点,Jackson 是 Spring Boot 内置的 Json 解析框架,用来完成出入参的序列化和反序列化。通常,我们会在 Controller 类中方法上,加上 @RequestBody 或者 @ResponseBody 注解,Spring Boot 会自动对出入参做 Json 解析与转换工作。

注意:@RequestBody用于将入参 Json 转换成对象,而 @ResponseBody 用于将对象转换成 Json 返回。

如果你跟前端约定好的字段是isDeleted,没想到这里序列化之后返回给前端的是deleted,这就会导致前端取不到值。

我们来看一下jackson的源码:

 /**
 * @since 2.5
 */
public static String okNameForRegularGetter(AnnotatedMethod am, String name,
        boolean stdNaming)
{
    if (name.startsWith("get")) {
        if ("getCallbacks".equals(name)) {
            if (isCglibGetCallbacks(am)) {
                return null;
            }
        } else if ("getMetaClass".equals(name)) {
            if (isGroovyMetaClassGetter(am)) {
                return null;
            }
        }
        return stdNaming
                ? stdManglePropertyName(name, 3)
                : legacyManglePropertyName(name, 3);
    }
    return null;
}




/**
 * @since 2.5
 */
public static String okNameForIsGetter(AnnotatedMethod am, String name,
        boolean stdNaming)
{
    if (name.startsWith("is")) { // plus, must return a boolean
        Class<?> rt = am.getRawType();
        if (rt == Boolean.class || rt == Boolean.TYPE) {
            return stdNaming
                    ? stdManglePropertyName(name, 2)
                    : legacyManglePropertyName(name, 2);
        }
    }
    return null;
}




/**
 * @since 2.5
 */
@Deprecated // since 2.9, not used any more
public static String okNameForSetter(AnnotatedMethod am, boolean stdNaming) {
    String name = okNameForMutator(am, "set", stdNaming);
    if ((name != null)
        // 26-Nov-2009, tatu: need to suppress this internal groovy method
            && (!"metaClass".equals(name) || !isGroovyMetaClassSetter(am))) {
        return name;
    }
    return null;
}




/**
 * @since 2.5
 */
public static String okNameForMutator(AnnotatedMethod am, String prefix,
        boolean stdNaming) {
    String name = am.getName();
    if (name.startsWith(prefix)) {
        return stdNaming
                ? stdManglePropertyName(name, prefix.length())
                : legacyManglePropertyName(name, prefix.length());
    }
    return null;
}

可以看出,如果是Boolean类型的变量,会将isDeleted的前缀is去掉,剩下的deleted作为反序列化key,而将setDeleted的前缀set去掉,剩下的deleted作为序列化key。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]