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.