JavaFXWebView不适用于SpringBootMaven
我已经用scene builder创建了Spring boot JavaFX应用程序。在FXML文件中添加WebView组件时,出现以下错误:JavaFXWebView不适用于SpringBootMaven,java,spring-boot,javafx,javafx-webengine,javafx-webview,Java,Spring Boot,Javafx,Javafx Webengine,Javafx Webview,我已经用scene builder创建了Spring boot JavaFX应用程序。在FXML文件中添加WebView组件时,出现以下错误: Caused by: javafx.fxml.LoadException: /C:/Users/pdhasar/WebProject/standalone-app/target/classes/WebView.fxml:12 at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader
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
WebViewController:
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:
spring似乎正在尝试访问WebView,javafx中有一个限制,不允许spring访问它。有没有办法让WebView与spring boot一起工作。谁能帮我解决这个问题。我终于找到了解决办法。在我将下面的代码添加到init方法中之后,它就工作了
Platform.runLater(() -> { ... });
JavaFX线程的背景 JavaFX系统的一个限制是,并非所有控件都可以脱离JavaFX应用程序线程构造。当给定控件或UI元素出现这种情况时,其文档会对此进行注释 特别是,各国的文件: 必须仅从FX线程创建和访问WebView对象 JavaFX应用程序的
init()
方法不在JavaFX应用程序线程上运行。在我的环境(OSX,JavaFX13)中,JavaFX应用程序的init()
方法由一个名为“JavaFXLauncher”的线程调用。如果将以下代码放入init()
,可以看出:
将相同的代码放在start()
方法中表明它运行在另一个名为“JavaFX应用程序线程”的线程上。这是创建WebView的正确线程
FXML加载程序基于FXML文件构造对象。如果您试图从JavaFX应用程序线程加载FXML,并且它包含必须在JavaFX应用程序线程上构造的某种控件,那么您将失败(如您所见)。大多数控件都可以在JavaFX线程之外构建,所以大多数情况下这不是问题。但是,WebView
必须在JavaFX应用程序线程上构建,因此您不能在JavaFX应用程序线程中加载带有WebView
的FXML文档
从start()
非init()
加载包含WebView引用的FXML
解决方案是将加载包含WebView的FXML的位置从init()
方法移动到start()
方法(由kleopatra在注释中建议),如下所示:
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应用程序创建一个单独的类:
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();
}
}
@SpringBootApplication
public class MySpringApplication {
// spring application implementation.
}
并让您的JavaFX应用程序调用run()
在已使用特定spring应用程序类初始化的SpringApplicationBuilder
实例上运行(即在您的JavaFXinit()
方法中有):
否则,您可能会在运行时创建两个JavaFX应用程序实例(一个由JavaFX应用程序创建),另一个由Spring应用程序运行程序创建,这可能会造成混淆。另外,通过将两者分离,您可以通过更强的关注点分离获得更好的设计,JavaFX应用程序负责处理JavaFX应用程序生命周期事件,而Spring应用程序负责处理Spring生命周期事件
然后,您可以完全独立于JavaFX应用程序启动和测试spring应用程序,这将使代码的测试和分析更加容易。hmm。。。想知道为什么要使用init方法(该方法被记录为从fx应用程序线程调用),而不是将其包装到runlater中(将其排队以便在fx应用程序线程上执行),您可以将其移动到start(该方法被记录为在fx线程上调用),或者为什么不这样做?它在init中失败,而不是在start中失败。spring线程正在尝试初始化和加载元素,但失败。Praveen,没有“spring线程”。在我的环境(OSX,JavaFX13)中,JavaFX应用程序的
init()
方法由名为JavaFXLauncher
的线程调用。从init()
=>System.out.println(“线程名称:”+Thread.currentThread().getName())中的以下代码可以看出:代码>。
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();
}
}
@SpringBootApplication
public class MySpringApplication {
// spring application implementation.
}
SpringApplicationBuilder builder = new SpringApplicationBuilder(
MySpringApplication.class
);
context = builder.run(
getParameters().getRaw().toArray(new String[0])
);