FXML从控制器读取变量时触发IllegalAccessException异常
问题:使用控制器变量设置Pane尺寸时抛出IllegalAccessException异常
尝试用控制器中的变量设置Pane的尺寸,却一直抛出以下异常:
java.lang.IllegalAccessException: class javafx.fxml.FXMLLoader$ValueElement (in module javafx.fxml) cannot access a member of class blub.Blub with modifiers "private"
编辑说明
已制作最小可复现示例,并且根据建议做了调整,但仍出现相同的IllegalAccessException异常。
startFXMain.java
public class startFXMain extends Application { private static MRE myApp; public static void main(String[] args) { try { myApp = MRE.getInstance(args); Application.launch(args); } catch (Exception e) { e.printStackTrace(); } } @Override public void start(Stage primaryStage) throws Exception { myApp.start(primaryStage); } }
MRE.java
public class MRE extends Application { private static MRE instance = null; @Override public void start(Stage startStage) throws Exception { try { new MREGUI(startStage); } catch (Exception e) { e.printStackTrace(); } } public static MRE getInstance(String[] args) { if (Objects.nonNull(instance)) return instance; else synchronized (MRE.class) { if (Objects.isNull(instance)) instance = new MRE(); return instance; } } public static MRE getInstance() { return instance; } private MRE() { } }
MREGUI.java
public class MREGUI extends Application { private GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice(); public int iScreenWidth = gd.getDisplayMode().getWidth(); public int iScreenHeight = gd.getDisplayMode().getHeight(); private Scene scMainScene; private Pane pMainPane; @Override public void start(Stage stgMainStage) throws Exception { this.initStage(stgMainStage); this.initStartPane(); this.initStartScene(this.pMainPane); stgMainStage.setScene(this.scMainScene); stgMainStage.show(); } private void initStage(Stage s) { s.setX(0); s.setY(0); s.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent e) { Platform.exit(); System.exit(0); } }); } private void initStartPane() { FXMLLoader loader = new FXMLLoader(); try { URL xmlUrl = new File(System.getProperty("user.dir") + "\\src\\startwindow.fxml").toURI().toURL(); loader.setLocation(xmlUrl); pMainPane = loader.load(); loader.setController(this); } catch (MalformedURLException err) { err.printStackTrace(); } catch (IOException err) { err.printStackTrace(); } } private void initStartScene(Pane rootPane) { scMainScene = new Scene(rootPane, iScreenHeight, iScreenWidth); } public MREGUI(Stage stgMainStage) { try { this.start(stgMainStage); } catch (Exception err) { err.printStackTrace(); } } private MREGUI() { } @FXML public int getIScreenWidth() { return this.iScreenWidth; } @FXML public int getIScreenHeight() { return this.iScreenHeight; } }
startwindow.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.layout.Pane?> <Pane layoutX="0" layoutY="0" minHeight="640" minWidth="480" prefHeight="${iScreenHeight}" prefWidth="${iScreenWidth}" xmlns:fx="http://javafx.com/fxml" fx:controller="MREGUI"> <style> -fx-background-color: blue; </style> </Pane>
解决方案
1. 修正FXMLLoader控制器设置顺序
当前代码先调用loader.load()再设置控制器,这会导致FXMLLoader读取FXML中的fx:controller属性创建新实例,后续设置的控制器无法生效,进而引发访问权限异常。
正确顺序是先设置控制器,再加载FXML:
private void initStartPane() { FXMLLoader loader = new FXMLLoader(); try { URL xmlUrl = new File(System.getProperty("user.dir") + "\\src\\startwindow.fxml").toURI().toURL(); loader.setLocation(xmlUrl); loader.setController(this); // 先绑定控制器 pMainPane = loader.load(); // 再加载FXML文件 } catch (MalformedURLException err) { err.printStackTrace(); } catch (IOException err) { err.printStackTrace(); } }
同时删除FXML中的fx:controller="MREGUI"属性,避免重复创建控制器:
<Pane layoutX="0" layoutY="0" minHeight="640" minWidth="480" prefHeight="${iScreenHeight}" prefWidth="${iScreenWidth}" xmlns:fx="http://javafx.com/fxml"> <style> -fx-background-color: blue; </style> </Pane>
2. 移除MREGUI的Application继承
MREGUI不需要继承Application,启动逻辑已在startFXMain和MRE中处理,继承会导致生命周期冲突:
// 去掉extends Application public class MREGUI { private GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice(); public int iScreenWidth = gd.getDisplayMode().getWidth(); public int iScreenHeight = gd.getDisplayMode().getHeight(); private Scene scMainScene; private Pane pMainPane; // 原start方法改名,去掉@Override注解 public void initAndShowStage(Stage stgMainStage) throws Exception { this.initStage(stgMainStage); this.initStartPane(); this.initStartScene(this.pMainPane); stgMainStage.setScene(this.scMainScene); stgMainStage.show(); } private void initStage(Stage s) { s.setX(0); s.setY(0); s.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent e) { Platform.exit(); System.exit(0); } }); } // 其余代码保持不变... }
然后修改MREGUI的构造函数:
public MREGUI(Stage stgMainStage) { try { this.initAndShowStage(stgMainStage); } catch (Exception err) { err.printStackTrace(); } }
3. 优化文件路径兼容性
使用跨平台的路径构建方式替代硬编码的Windows分隔符:
Path fxmlPath = Paths.get(System.getProperty("user.dir"), "src", "startwindow.fxml"); URL xmlUrl = fxmlPath.toUri().toURL();
内容的提问来源于stack exchange,提问作者thebert
相关产品推荐
相关产品推荐

