xxSkin 中的深度反射失败 类
Deep reflection fails in xxSkin classes
自更新 9-u175 以来,java 默认允许非法访问,从而允许所有旧的反射技巧。工作正常,除了涉及 control.skin 中的 类 时(也许其他人也一样,没有检查) - 要重现,运行 下面的示例,单击按钮并查看如何访问成功直到尝试访问 ButtonSkin 中的私有字段的行。堆栈跟踪:
Exception in thread "JavaFX Application Thread" java.lang.reflect.InaccessibleObjectException:
Unable to make field private final com.sun.javafx.scene.control.behavior.BehaviorBase javafx.scene.control.skin.ButtonSkin.behavior accessible:
module javafx.controls does not "opens javafx.scene.control.skin" to unnamed module @537fb2
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
我的上下文:jdk9-u175,带有 java9 补丁的 eclipse-oxygen-R,项目中的访问规则设置为允许 javafx/**
问题是:谁是罪魁祸首? FX、Eclipse、ea 还是 ..?
例子:
import java.lang.reflect.Field;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SkinBase;
import javafx.scene.control.skin.ButtonSkin;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
public class AccessFieldFX extends Application {
private Parent getContent() {
Button button = new Button("something to click on");
// okay
Object def = invokeGetFieldValue(Button.class, button, "defaultButton");
button.setOnAction(e -> {
ButtonSkin skin = (ButtonSkin) button.getSkin();
// okay
LambdaMultiplePropertyChangeListenerHandler cl =
(LambdaMultiplePropertyChangeListenerHandler) invokeGetFieldValue(SkinBase.class, skin, "lambdaChangeListenerHandler");
// okay
Object clField = invokeGetFieldValue(LambdaMultiplePropertyChangeListenerHandler.class, cl, "EMPTY_CONSUMER");
// failure
Object beh = invokeGetFieldValue(ButtonSkin.class, skin, "behavior");
});
BorderPane pane = new BorderPane(button);
return pane;
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent(), 600, 400));
// primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static Object invokeGetFieldValue(Class declaringClass, Object target, String name) {
try {
Field field = declaringClass.getDeclaredField(name);
field.setAccessible(true);
return field.get(target);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(AccessFieldFX.class.getName());
}
为了防止对新 API 的意外依赖,非法访问仅授予 Java 之前存在的包 9 - 因此我假设 com.sun.javafx.scene.control.behavior
是新的。
Mark Reinhold 在他的 mail with the revised proposal for --illegal-access
中写道(强调我的):
--illegal-access=permit
This mode opens each package in each module in the run-time image to
code in all unnamed modules, i.e., code on the class path, if that
package existed in JDK 8. This enables both static access, i.e., by
compiled bytecode, and deep reflective access, via the platform's
various reflection APIs.
The first reflective-access operation to any such package causes a
warning to be issued, but no warnings are issued after that point.
This single warning describes how to enable further warnings.
This mode will be the default for JDK 9. It will be removed in a
future release.
自更新 9-u175 以来,java 默认允许非法访问,从而允许所有旧的反射技巧。工作正常,除了涉及 control.skin 中的 类 时(也许其他人也一样,没有检查) - 要重现,运行 下面的示例,单击按钮并查看如何访问成功直到尝试访问 ButtonSkin 中的私有字段的行。堆栈跟踪:
Exception in thread "JavaFX Application Thread" java.lang.reflect.InaccessibleObjectException:
Unable to make field private final com.sun.javafx.scene.control.behavior.BehaviorBase javafx.scene.control.skin.ButtonSkin.behavior accessible:
module javafx.controls does not "opens javafx.scene.control.skin" to unnamed module @537fb2
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
我的上下文:jdk9-u175,带有 java9 补丁的 eclipse-oxygen-R,项目中的访问规则设置为允许 javafx/**
问题是:谁是罪魁祸首? FX、Eclipse、ea 还是 ..?
例子:
import java.lang.reflect.Field;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SkinBase;
import javafx.scene.control.skin.ButtonSkin;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
public class AccessFieldFX extends Application {
private Parent getContent() {
Button button = new Button("something to click on");
// okay
Object def = invokeGetFieldValue(Button.class, button, "defaultButton");
button.setOnAction(e -> {
ButtonSkin skin = (ButtonSkin) button.getSkin();
// okay
LambdaMultiplePropertyChangeListenerHandler cl =
(LambdaMultiplePropertyChangeListenerHandler) invokeGetFieldValue(SkinBase.class, skin, "lambdaChangeListenerHandler");
// okay
Object clField = invokeGetFieldValue(LambdaMultiplePropertyChangeListenerHandler.class, cl, "EMPTY_CONSUMER");
// failure
Object beh = invokeGetFieldValue(ButtonSkin.class, skin, "behavior");
});
BorderPane pane = new BorderPane(button);
return pane;
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent(), 600, 400));
// primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static Object invokeGetFieldValue(Class declaringClass, Object target, String name) {
try {
Field field = declaringClass.getDeclaredField(name);
field.setAccessible(true);
return field.get(target);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(AccessFieldFX.class.getName());
}
为了防止对新 API 的意外依赖,非法访问仅授予 Java 之前存在的包 9 - 因此我假设 com.sun.javafx.scene.control.behavior
是新的。
Mark Reinhold 在他的 mail with the revised proposal for --illegal-access
中写道(强调我的):
--illegal-access=permit
This mode opens each package in each module in the run-time image to code in all unnamed modules, i.e., code on the class path, if that package existed in JDK 8. This enables both static access, i.e., by compiled bytecode, and deep reflective access, via the platform's various reflection APIs.
The first reflective-access operation to any such package causes a warning to be issued, but no warnings are issued after that point. This single warning describes how to enable further warnings.
This mode will be the default for JDK 9. It will be removed in a future release.