杰克逊私人建筑商,JDK 9+,龙目岛

时间:2018-07-26 20:41:48

标签: jackson lombok jackson2 jackson-databind

我正在寻找有关Jackson如何与不可变类型的私有构造函数一起使用的文档。使用Jackson 2.9.6和Spring Boot提供的默认对象映射器(两个以jdk-10.0.1运行)

给出JSON:

{"a":"test"} 

并给一个类似的类:

public class ExampleValue {

    private final String a;

    private ExampleValue() {
        this.a = null;
    }

    public String getA() {
        return this.a;
    }
}

反序列化(至少对我来说令人惊讶)似乎有效。

而事实并非如此:

public class ExampleValue {

    private final String a;

    private ExampleValue(final String  a) {
        this.a = a;
    }

    public String getA() {
        return this.a;
    }
}

这样做:

public class ExampleValue {

    private final String a;

    @java.beans.ConstructorProperties({"a"})
    private ExampleValue(final String a) {
        this.a = a;
    }

    public String getA() {
        return this.a;
    }
}

我的假设是,第一个示例可以工作的唯一方法是使用反射来设置最终字段的值(我猜想java.lang.reflect.AccessibleObject.setAccessible(true)会这样做)。

问题1:在这种情况下,杰克逊的工作方式是正确的吗?我认为这在不允许执行此操作的安全管理器下可能会失败?

因此,我个人的偏爱将是上面的最后一个代码示例,因为它涉及的“魔术”较少,并且在安全管理器下工作。但是,我对Lombok和用于默认生成@java.beans.ConstructorProperties(...)的构造函数生成的各种线程感到有些困惑,但是后来更改为默认,不再执行此操作,现在允许使用{{ 1}}

有些人(包括龙目岛release notes中的lombok.anyConstructor.addConstructorProperties=true)建议:

  

Oracle在JDK9发行版中或多或少地破坏了此注释,因此有必要进行这一重大更改。

但是我不清楚这是什么意思,Oracle破坏了什么?对于使用杰克逊2.9.6的JDK 10的我来说似乎可以正常工作。

问题2:是否有人能够阐明该注释在JDK 9中是如何被破坏的以及为什么lombok现在认为不再需要默认生成该注释?

1 个答案:

答案 0 :(得分:3)

答案1:这就是它的工作原理(也令我惊讶)。根据{{​​3}},属性INFER_PROPERTY_MUTATORSALLOW_FINAL_FIELDS_AS_MUTATORSCAN_OVERRIDE_ACCESS_MODIFIERS均默认为true。因此,在您的第一个示例中,杰克逊

  • 借助AccessibleObject#setAccessibleCAN_OVERRIDE_ACCESS_MODIFIERS)使用私有构造函数创建实例,
  • 为(私有)字段检测完全可访问的getter方法,并将该字段视为可变属性(INFER_PROPERTY_MUTATORS),
  • 由于final而忽略了字段上的ALLOW_FINAL_FIELDS_AS_MUTATORS,并且
  • 使用AccessibleObject#setAccessibleCAN_OVERRIDE_ACCESS_MODIFIERS)访问该字段。

但是,我同意不应依赖它,因为正如您所说,安全管理器可以禁止它,否则Jackson的默认设置可能会更改。此外,我感觉“不正确”,因为我希望该类是不变的,并且该字段是不可设置的。

示例2不起作用,因为Jackson找不到可用的构造函数(因为它无法将字段名称映射到唯一现有构造函数的参数名称,因为这些名称在运行时不存在)。在您的第三个示例中,@java.beans.ConstructorProperties绕过了这个问题,因为Jackson在运行时显式地寻找该注释。

答案2: 我的解释是@java.beans.ConstructorProperties并没有真正被打破,但是不能认为Java 9+可以再出现java.desktop了。这是由于其在java.desktop模块中的成员身份(有关此主题的讨论,请参见Jackson documentation on Mapper Features)。由于模块化Java应用程序可能具有不带此模块的模块路径,因此lombok如果默认情况下会生成此注释,则会破坏此类应用程序。 (此外,该注释通常在Android SDK上不可用。)

因此,如果您有一个非模块化应用程序或模块路径上带有lombok.anyConstructor.addConstructorProperties=true的模块化应用程序,最好是通过设置vColor让lombok生成注释,或者手动添加注释如果您不使用lombok。