我正在寻找有关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现在认为不再需要默认生成该注释?
答案 0 :(得分:3)
答案1:这就是它的工作原理(也令我惊讶)。根据{{3}},属性INFER_PROPERTY_MUTATORS
,ALLOW_FINAL_FIELDS_AS_MUTATORS
和CAN_OVERRIDE_ACCESS_MODIFIERS
均默认为true
。因此,在您的第一个示例中,杰克逊
AccessibleObject#setAccessible
(CAN_OVERRIDE_ACCESS_MODIFIERS
)使用私有构造函数创建实例,INFER_PROPERTY_MUTATORS
),final
而忽略了字段上的ALLOW_FINAL_FIELDS_AS_MUTATORS
,并且AccessibleObject#setAccessible
(CAN_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。