首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >JavaFX WebView无法与spring boot maven一起使用。

JavaFX WebView无法与spring boot maven一起使用。
EN

Stack Overflow用户
提问于 2019-10-07 16:50:05
回答 2查看 1.2K关注 0票数 1

我已经使用场景构建器创建了Spring boot JavaFX应用程序。当我在我的FXML文件中添加WebView组件时,我得到了以下错误:

代码语言:javascript
运行
复制
Caused by: javafx.fxml.LoadException: 
/C:/Users/pdhasar/WebProject/standalone-app/target/classes/WebView.fxml:12

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at com.web.view.WebViewApplication.init(WebViewApplication.java:29)
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:841)
    ... 3 more
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.JavaFXBuilder$ObjectBuilder.build(JavaFXBuilderFactory.java:278)
    at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:759)
    at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2827)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2536)
    ... 7 more
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.JavaFXBuilder$ObjectBuilder.build(JavaFXBuilderFactory.java:270)
    ... 10 more
Caused by: java.lang.IllegalStateException: Not on FX application thread; currentThread = JavaFX-Launcher
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:438)
    at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1182)
    at javafx.scene.web.WebEngine.<init>(WebEngine.java:822)
    at javafx.scene.web.WebEngine.<init>(WebEngine.java:811)
    at javafx.scene.web.WebView.<init>(WebView.java:271)
    at javafx.scene.web.WebViewBuilder.build(WebViewBuilder.java:60)
    ... 21 more
2019-10-07 14:00:12.979  INFO 14680 --- [       Thread-6] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7f904074: startup date [Mon Oct 07 14:00:10 IST 2019]; root of context hierarchy

下面给出了spring boot的代码: WebViewApplication:

代码语言:javascript
运行
复制
package com.web.view;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.web.view")
public class WebViewApplication extends Application {

    private ConfigurableApplicationContext context;
    private Parent rootNode;

    @Override
    public void init() throws Exception {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(WebViewApplication.class);
        context = builder.run(getParameters().getRaw().toArray(new String[0]));
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/WebView.fxml"));
        loader.setControllerFactory(context::getBean);
        rootNode = loader.load();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
        double width = visualBounds.getWidth();
        double height = visualBounds.getHeight();

        primaryStage.setScene(new Scene(rootNode, width, height));
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

    @Override
    public void stop() throws Exception {
        context.close();
    }
}

WebViewController:

代码语言:javascript
运行
复制
package com.web.view.controller;

import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import org.springframework.stereotype.Controller;

@Controller
public class WebViewController {

    @FXML private WebView webView;
    private WebEngine webEngine;

    @FXML
    private void handleWebViewButtonAction(ActionEvent event) {
        webEngine = webView.getEngine();
        webEngine.load("https://www.google.co.in");
    }
}

FXML:

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.web.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.web.view.controller.WebViewController">
   <children>
      <WebView fx:id="webView" layoutX="-116.0" layoutY="-111.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
   </children>
</AnchorPane>

看起来spring正在尝试访问WebView,而且在javafx中有一个限制,不允许spring访问它。有没有办法让WebView和spring boot一起工作。有人能帮我解决这个问题吗?

EN

回答 2

Stack Overflow用户

发布于 2019-10-08 05:46:54

JavaFX线程的背景

并非所有控件都可以在JavaFX应用程序线程之外构造,这是JavaFX系统的一个限制。当给定的控件或UI元素出现这种情况时,其文档会注意到这一点。

特别是,WebView的文档说明:

必须仅从FX线程创建和访问

WebView对象。

JavaFX应用程序的init()方法不在JavaFX应用程序线程上运行。在我的环境(OS X、JavaFX 13)中,JavaFX应用程序的init()方法由一个名为"JavaFX-Launcher“的线程调用。如果您将以下代码放在init()中,就可以看到

代码语言:javascript
运行
复制
System.out.println("Thread name: " + Thread.currentThread().getName());

将相同的代码放在start()方法中会显示它运行在另一个名为"JavaFX Application“的线程上。这是在其上创建WebView的正确线程。

FXML加载器基于FXML文件构造对象。如果您试图从JavaFX应用程序线程加载FXML,并且它包含一种必须在JavaFX应用程序线程上构造的控件,您将得到一个失败(如您所见)。大多数控件都可以在JavaFX线程之外构造,因此在大多数情况下这不是问题。但是,WebView必须在JavaFX应用程序线程上构造,因此不能在JavaFX应用程序线程外使用WebView加载FXML文档。

从WebView init()而不是加载包含引用的FXML

解决方案是将加载包含WebView的FXML的位置从init()方法移动到start()方法(由kleopatra在注释中建议),如下所示:

代码语言:javascript
运行
复制
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.web.view")
public class WebViewApplication extends Application {

    private ConfigurableApplicationContext context;
    private Parent rootNode;

    @Override
    public void init() throws Exception {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(WebViewApplication.class);
        context = builder.run(getParameters().getRaw().toArray(new String[0]));
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/WebView.fxml"));
        loader.setControllerFactory(context::getBean);
        rootNode = loader.load();

        Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
        double width = visualBounds.getWidth();
        double height = visualBounds.getHeight();

        primaryStage.setScene(new Scene(rootNode, width, height));
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

    @Override
    public void stop() throws Exception {
        context.close();
    }
}

无关设计建议

顺便说一下,我建议不要让你的JavaFX应用程序成为Spring应用程序。

相反,应该为Spring应用程序创建一个单独的类:

代码语言:javascript
运行
复制
@SpringBootApplication
public class MySpringApplication {
    // spring application implementation.
}

并让您的JavaFX应用程序在用您的特定spring应用程序类初始化的SpringApplicationBuilder实例上调用run() (即,在您的JavaFX init()方法中):

代码语言:javascript
运行
复制
SpringApplicationBuilder builder = new SpringApplicationBuilder(
    MySpringApplication.class
);
context = builder.run(
    getParameters().getRaw().toArray(new String[0])
);

否则,您可能有两个在运行时创建的JavaFX应用程序实例(一个由JavaFX应用程序创建),另一个由Spring应用程序运行器创建,这可能会造成混淆。此外,通过将两者分开,您可以通过更强的关注点分离获得更好的设计,JavaFX应用程序负责处理JavaFX应用程序生命周期事件,Spring应用程序负责处理spring生命周期事件。

然后,您可以完全独立于JavaFX应用程序启动和测试spring应用程序,这将使代码的测试和分析变得更容易。

票数 1
EN

Stack Overflow用户

发布于 2019-10-07 17:46:44

最后,我得到了解决方案。在我将下面的代码添加到init方法中之后,它就可以工作了

代码语言:javascript
运行
复制
Platform.runLater(() -> { ... });
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58266201

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档