eclipselink 静态编织与 Java 9 上的最终字段



>我有一些JPA注释字段声明为最终的,如下所示:

@Column(name = "SOME_FIELD", updatable = false, nullable = false)
private final String someField;

将实体插入数据库时,这些字段将存储在数据库中。它们无法进一步更新。对于Java编程语言,这些字段可以被认为是最终的。

使用 EclipseLink 静态编织插件,由于一些字节码检测,可以延迟加载实体。

我不知道这样的结构(最终字段)在 JPA 中是否正式允许,但我喜欢使用它们,因为它们在编译时强制这些字段不打算更新。

在Java 8中,使用这种结构构建的程序运行良好。但是在Java 9中,当涉及EclipseLink静态编织时,我得到以下运行时异常:

Caused by: java.lang.IllegalAccessError: Update to non-static final field xxx.yyy.SomeEntity.someField attempted from a different method (_persistence_set) than the initializer method <init> 

这样的错误似乎是由于以下JVM规范造成的:

否则,如果字段是 final,则必须在当前 类,并且指令必须发生在实例初始化中 当前类的方法 ()。否则,非法访问错误 被扔了。

因此,我觉得一些编织工具需要一些更新才能满足这个JVM规范。EclipseLink 静态编织工具似乎是其中之一。

问题

  • JPA 中是否允许使用最终字段构造(此处介绍的那些)?
  • 是否有 JDK 9 选项可以在其他位置禁用对最终字段分配的检查,而不仅仅是在类的实例初始化方法() 中(作为临时解决方法)?

编辑

根据 JPA 规范,JPA 中不允许使用最终字段:

实体类不得是最终的。实体类的任何方法或持久实例变量都不能是最终的。

JPA 中是否允许使用此处介绍的最终字段构造?

如发行说明 JDK-8157181 中所述,还引入了一项更改,以限制编译器接受初始值设定项方法之外的最终字段的修改。


根据 Java VM 规范,putstatic字节码仅允许修改final字段

  1. 如果字段在当前类(声明当前方法的类)中声明,并且
  2. 如果putstatic指令出现在当前类classinterface初始值设定项方法<clinit>中。

否则,必须抛出IllegalAccessError

同样,仅允许putfield字节码修改final字段

  1. 如果该字段在当前类中声明,并且
  2. 如果指令出现在当前类的实例初始值设定项方法中。

否则,必须抛出IllegalAccesError

不满足条件 (2) 的方法违反了编译器的假设,并一直处于检查之下,以便在 Java 9 中实现所需的条件。

您可以按照错误报告进行操作以获取详细说明。


是否有 JDK 9 选项可以在其他位置禁用对最终字段分配的检查,而不仅仅是在类的实例初始化方法() 中(作为临时解决方法)?

在 JDK 9 版本中,HotSpot VM 完全强制执行以前的 提到的限制,但仅适用于版本号为>= 53(Java 9) 的类文件。对于版本号<53 的类文件,仅部分强制实施限制(因为这是由发行版完成的 在 JDK 9 之前)。也就是说,对于版本号为<的类文件>53final字段可以在类的任何方法中修改 声明字段(不仅是类/实例初始值设定项)。

因此,您可以尝试使用源代码和目标 1.8 编译代码,以检查现在是否可以解决问题。这应该是可以使用最新的javac工具--release 8选项来实现的。

请阅读 JPA 规范 - http://download.oracle.com/otndocs/jcp/persistence-2_2-mrel-eval-spec/index.html - 第 2.1 节 实体类:

实体类不得是最终的。实体类的任何方法或持久实例变量都不能是最终的。

方法 _persistence_set 是通过编织(实体类的字节码操作)添加的代码,用于在通过默认构造函数(无属性)调用创建类后初始化实体属性的值。Java 1.8 允许这样做,即使是最终字段,但 Java 9 没有。

您现在应该遵循 JPA 规范,并且不应将最终的持久性属性放入您的实体中。

Eclipselink 不承认这一点,但是,我希望我的项目这样做。所以我做了一个肮脏的"黑客"来做到这一点。基本上,我已经覆盖了eclipselink类org.eclipse.persistence.internal.jpa.weaving.ClassWeaver,并添加了此方法:

@Override
public FieldVisitor visitField(
final int access,
final String name,
final String descriptor,
final String signature,
final Object value) {
if (cv != null) {
int newAccess = access;
if (!Modifier.isStatic(access) && Modifier.isFinal(access)) {
newAccess = access & (~Opcodes.ACC_FINAL);
}
return cv.visitField(newAccess, name, descriptor, signature, value);
}
return null;
}

这将在编织时从实体上的非静态字段中删除所有最终修饰符。

我认为 eclipselink 应该考虑添加它,至少作为一种选择。

最新更新