JavaFX打包exe后Selenium ChromeDriver启动挂起问题排查
JavaFX+Selenium应用jpackage打包exe后ChromeDriver挂起问题
问题描述
开发的JavaFX应用集成Selenium实现网页爬取,在IntelliJ本地运行一切正常,但通过jpackage打包为Windows exe后,除ChromeDriver相关功能外其余模块均正常。代码总会卡在new ChromeDriver()处,无任何报错信息,最后一条调试日志显示已找到驱动路径:
SeleniumUtil - next line is new ChromeDriver(). Driver system location: C:\Users\cdavi\.cache\selenium\chromedriver\win64\144.0.7559.133\chromedriver.exe
环境与已尝试方案
当前环境
使用Selenium 4.40.0版本,Gradle依赖配置:
implementation("org.seleniumhq.selenium:selenium-java:4.40.0")
已尝试的解决方案
- 显式设置chromedriver.exe路径:
System.setProperty("webdriver.chrome.driver", "C:\\dev\\chromedriver.exe");
- 切换至Selenium 4.6.0版本(该版本宣称内置Driver Manager,无需手动指定驱动路径),但4.6版本运行时会报错
"The path to the driver executable must be set by the webdriver.chrome.driver system property;",4.40版本无此报错但仍存在挂起问题 - 引入bonigarcia的webdrivermanager依赖:
implementation("io.github.bonigarcia:webdrivermanager:6.3.3")
上述方案均无效,代码始终卡在new ChromeDriver()处。
对应的工具类代码:
public final class SeleniumUtil { private static final Logger logger = LoggerFactory.getLogger(SeleniumUtil.class); public static ScrapeResult getDocument(final String url) { WebDriverManager.chromedriver().setup(); WebDriver driver = null; ScrapeResult scrapeResult = new ScrapeResult(url); try { ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); // Modern headless flag options.addArguments("--disable-gpu"); // Recommended for headless options.addArguments("--disable-extensions"); // Recommended for headless options.addArguments("--disable-background-networking"); // Recommended for headless options.addArguments("--window-size=1280,800"); // Define a consistent screen size options.addArguments("--remote-debugging-pipe"); options.addArguments("--disable-blink-features=AutomationControlled"); options.setExperimentalOption("excludeSwitches", Collections.singleton("enable-automation")); logger.debug("next line is new ChromeDriver(). Driver system location: " + System.getProperty("webdriver.chrome.driver")); driver = new ChromeDriver(options); logger.debug("finished creating chromedriver. Driver system location: " + System.getProperty("webdriver.chrome.driver")); //driver.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"); logger.debug("Retrieve from " + url); driver.get(url); logger.debug("Done!"); scrapeResult.parse(driver.getPageSource()); } catch (Exception e) { scrapeResult.setException(e); logger.error("Selenium request failed: " + e.getMessage(), e); } finally { if (driver != null) { driver.quit(); // CRITICAL: Always quit the driver } } return scrapeResult; } private SeleniumUtil() {} }
评论建议后的更新
移除bonigarcia:webdrivermanager依赖并注释相关代码后,IDE本地运行仍正常,但打包为exe后依旧卡在new ChromeDriver()处,日志显示Driver system location: null。已关闭微软防火墙排除网络问题,设置webdriver.chrome.logfile属性但打包后未生成任何日志。
更新#2:最小复现示例
该示例可复现问题,推测问题与在JavaFX的Task后台线程中运行ChromeDriver代码有关:
- 第一个按钮直接在GUI线程执行ChromeDriver代码(出现日志类找不到错误,非核心问题)
- 第二个按钮在
Task后台线程中调用ChromeDriver代码(JavaFX处理长任务的标准方式),出现与原程序完全一致的问题——卡在new ChromeDriver()处,无任何日志输出。
相关文件代码
build.gradle
plugins { id 'java' id 'application' id 'org.openjfx.javafxplugin' version '0.0.13' id 'org.beryx.runtime' version '2.0.1' } group 'my.selenium' version '1.0-SNAPSHOT' repositories { mavenCentral() } ext { junitVersion = '5.10.2' } java { sourceCompatibility = JavaVersion.VERSION_22 targetCompatibility = JavaVersion.VERSION_22 } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } application { applicationName = 'MySeleniumFX' mainClass = 'my.selenium.myseleniumfx.HelloApplication' } javafx { version = '22' modules = ['javafx.controls', 'javafx.fxml'] } dependencies { implementation("org.seleniumhq.selenium:selenium-java:4.40.0") testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") } test { useJUnitPlatform() } runtime { options = ['--strip-debug', '--compress', 'zip-6', '--no-header-files', '--no-man-pages'] jpackage { appVersion = '0.0.5' imageName = 'MySeleniumFX' imageOptions = ["--win-console"] installerOptions += ['--resource-dir', "src/main/resources"] installerOptions += ['--vendor', 'Me'] installerOptions += ['--name', 'MySeleniumFX'] installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu'] } }
SeleniumLoader.java
package my.selenium.myseleniumfx; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import java.util.Collections; public class SeleniumLoader { protected String process( final String url ) { WebDriver driver = null; String pageTitle = null; try { ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); // Modern headless flag options.addArguments("--disable-gpu"); // Recommended for headless options.addArguments("--disable-extensions"); // Recommended for headless //options.addArguments("--disable-background-networking"); // Recommended for headless options.addArguments("--window-size=1280,800"); // Define a consistent screen size //options.addArguments("--remote-debugging-pipe"); // 添加参数禁用AutomationControlled标识 options.addArguments("--disable-blink-features=AutomationControlled"); options.setExperimentalOption("excludeSwitches", Collections.singleton("enable-automation")); //options.setExperimentalOption("useAutomationExtension", false); System.out.println("About to create new ChromeDriver()"); System.out.println("System.getProperty('webdriver.chrome.driver') = " + System.getProperty("webdriver.chrome.driver")); driver = new ChromeDriver(options); System.out.println("new ChromeDriver() success!"); // 验证new ChromeDriver()是否设置了系统属性,结果未设置 System.out.println("System.getProperty('webdriver.chrome.driver') = " + System.getProperty("webdriver.chrome.driver")); System.out.println("Getting from " + url); driver.get(url); pageTitle = driver.getTitle(); System.out.println("Done! Page title = " + pageTitle); } catch (Exception ex) { ex.printStackTrace(System.out); } finally { if (driver != null) { driver.quit(); System.out.println("Driver has been quit"); } } return pageTitle; } }
HelloController.java
package my.selenium.myseleniumfx; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.scene.control.Label; public class HelloController { @FXML private Label buttonMessages; @FXML protected void onClickInsideThread() { buttonMessages.setText("Clicked on action to be run inside the gui thread"); String pageTitle = new SeleniumLoader().process("https://www.selenium.dev/documentation/"); buttonMessages.setText(pageTitle); } @FXML protected void onClickOutsideThread() { buttonMessages.setText("Clicked on action to be run outside the gui thread"); startBackgroundTask(); } private void startBackgroundTask() { Task<String> task = new Task<String>() { @Override protected String call() throws Exception { // 后台线程执行长任务 updateMessage("Task started..."); String pageTitle = new SeleniumLoader().process("https://www.selenium.dev/documentation/"); updateMessage("Task finished."); return pageTitle; } }; task.setOnSucceeded(event -> { // UI线程更新结果 buttonMessages.setText("Succeeded: " + task.getValue()); }); task.setOnFailed(event -> { // UI线程显示错误 buttonMessages.setText("Failed: " + task.getException().getMessage()); }); // 启动后台线程执行任务 new Thread(task).start(); } }
HelloApplication.java
package my.selenium.myseleniumfx; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.IOException; public class HelloApplication extends Application { @Override public void start(Stage stage) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml")); Scene scene = new Scene(fxmlLoader.load(), 320, 240); stage.setTitle("Hello!"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(); } }
hello-view.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <VBox alignment="CENTER" prefHeight="174.0" prefWidth="262.0" spacing="20.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/25" fx:controller="my.selenium.myseleniumfx.HelloController"> <padding> <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> </padding> <Label fx:id="buttonMessages" /> <Button onAction="#onClickInsideThread" text="Run on the thread" /> <Button mnemonicParsing="false" onAction="#onClickOutsideThread" text="Run outside the thread" /> </VBox>
内容的提问来源于stack exchange,提问作者chravis
相关产品推荐
相关产品推荐

