You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX+Afterburner.fx项目FXML文件加载失败问题求助

问题描述

执行mvn clean javafx:run时出现FXML文件加载失败错误:

File: recipelistpresenter.fxml not found, attempting with camel case
Cannot load file RecipeListPresenter.fxml
Stopping initialization phase...
Exception in Application start method
java.lang.reflect.InvocationTargetException
...

已确认RecipeListPresenter.fxml存在于target目录:

-rw-rw-r-- 1 amaury amaury 291 janv. 11 21:23 RecipeListPresenter.fxml

项目代码

Main.java

package com.example.eatti;

import com.airhacks.afterburner.injection.Injector;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import com.example.eatti.view.recipe.RecipeListPresenter;

public class Main extends Application {
    @Override
    public void start(Stage stage) {
        RecipeListPresenter recipeListPresenter = new RecipeListPresenter();
        Scene scene = new Scene(recipeListPresenter.getView());
        stage.setTitle("Recipe Manager");
        stage.setScene(scene);
        stage.show();
    }

    @Override
    public void stop() throws Exception {
        Injector.forgetAll(); // Cleanup Afterburner.fx injections
    }

    public static void main(String[] args) {
        launch(args);
    }
}

FXMLView.java

package com.example.eatti.view;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FXMLView {
    private static final Logger log = LoggerFactory.getLogger(FXMLView.class);

    public static void load(String fxmlPath, Stage stage) {
        try {
            log.debug("Loading FXML from path: {}", fxmlPath);
            FXMLLoader loader = new FXMLLoader(FXMLView.class.getResource(fxmlPath));
            Parent root = loader.load();
            log.debug("FXML successfully loaded: {}", fxmlPath);
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.show();
        } catch (Exception e) {
            log.error("Error loading FXML file: {}", fxmlPath, e);
            throw new RuntimeException(e);
        }
    }
}

RecipeListPresenter.java

package com.example.eatti.view.recipe;

import javax.inject.Inject;
import com.airhacks.afterburner.views.FXMLView;
import com.example.eatti.model.entity.Recipe;
import com.example.eatti.service.RecipeService;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.UUID;

public class RecipeListPresenter extends FXMLView {
    private static final Logger log = LoggerFactory.getLogger(RecipeListPresenter.class);

    @Inject
    private RecipeService recipeService;

    @FXML
    private ListView<Pair<UUID, String>> recipeListView;

    @FXML
    public void initialize() {
        loadRecipes();
        setupListView();
    }

    private void loadRecipes() {
        List<Recipe> recipes = recipeService.findAllRecipes();
        for (Recipe recipe : recipes) {
            Pair<UUID, String> recipeItem = new Pair<>(recipe.getId(), recipe.getName());
            recipeListView.getItems().add(recipeItem);
        }
    }

    private void setupListView() {
        recipeListView.setCellFactory(param -> new ListCell<Pair<UUID, String>>() {
            @Override
            protected void updateItem(Pair<UUID, String> item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getValue());
                }
            }
        });
        recipeListView.setOnMouseClicked(event -> {
            if (event.getClickCount() == 2) {
                handleRecipeDoubleClick(event);
            }
        });
    }

    private void handleRecipeDoubleClick(MouseEvent event) {
        if (event.getClickCount() == 2) {
            Pair<UUID, String> selectedRecipe = recipeListView.getSelectionModel().getSelectedItem();
            if (selectedRecipe != null) {
                UUID selectedRecipeId = selectedRecipe.getKey();
                Recipe recipe = recipeService.findRecipeById(selectedRecipeId);
                if (recipe != null) {
                    openRecipeDetails(recipe);
                }
            }
        }
    }

    private void openRecipeDetails(Recipe recipe) {
        log.debug("Opening recipe: {}", recipe.getName());
        Stage stage = new Stage();
        RecipeDetailsPresenter recipeDetailsPresenter = new RecipeDetailsPresenter();
        recipeDetailsPresenter.setRecipe(recipe);
        stage.setScene(new Scene(recipeDetailsPresenter.getView()));
        stage.show();
    }
}

目录结构

src/main/
├── java
│   └── com
│       └── example
│           └── eatti
│               ├── controller
│               ├── Main.java
│               ├── model
│               │   ├── entity
│               │   │   ├── Ingredient.java
│               │   │   ├── RecipeIngredient.java
│               │   │   ├── Recipe.java
│               │   │   ├── RecipeUnit.java
│               │   │   └── Unit.java
│               │   └── repository
│               │       ├── IngredientRepository.java
│               │       ├── RecipeIngredientRepository.java
│               │       ├── RecipeRepository.java
│               │       ├── RecipeUnitRepository.java
│               │       └── UnitRepository.java
│               ├── service
│               │   └── RecipeService.java
│               ├── util
│               │   └── HibernateUtil.java
│               └── view
│                   ├── FXMLView.java
│                   └── recipe
│                       ├── RecipeDetailsPresenter.java
│                       ├── RecipeFormPresenter.java
│                       └── RecipeListPresenter.java
└── resources
    ├── application.properties
    ├── com
    │   └── example
    │       └── eatti
    │           └── fxml
    │               ├── recipe-details.fxml
    │               ├── recipe-form.fxml
    │               └── RecipeListPresenter.fxml
    └── hibernate.cfg.xml

当前疑问:

  1. 如何检查当前的文件加载位置?
  2. 如何修改加载路径以适配项目结构需求?

解决方案

1. 检查当前文件加载位置

在RecipeListPresenter的initialize方法开头添加调试代码,打印程序实际查找资源的路径:

// 打印当前类查找FXML的路径
System.out.println("Looking for FXML at: " + getClass().getResource("RecipeListPresenter.fxml"));
// 打印类路径根目录
System.out.println("Classpath root: " + getClass().getResource("/"));

执行mvn clean javafx:run后,控制台会输出实际查找路径,对比target/classes/com/example/eatti/fxml/RecipeListPresenter.fxml的实际位置,即可定位路径差异。

另外也可以直接查看target/classes目录的结构,确认FXML文件的最终存放路径。

2. 修改加载路径适配项目结构

Afterburner.fx默认会在Presenter类所在包路径下查找同名FXML文件,而你的文件放在resources/com/example/eatti/fxml/目录下,需要手动指定路径:

方法一:重写getResource方法

在RecipeListPresenter中重写getResource()方法,指定正确的FXML路径:

@Override
public URL getResource() {
    return getClass().getResource("/com/example/eatti/fxml/RecipeListPresenter.fxml");
}

路径开头的/表示从类路径根目录(即target/classes)开始查找。

方法二:统一配置基础Presenter类

如果所有FXML都放在fxml目录下,可自定义基础Presenter类统一处理路径:

package com.example.eatti.view;

import com.airhacks.afterburner.views.FXMLView;
import java.net.URL;

public class BasePresenter extends FXMLView {
    @Override
    public URL getResource() {
        String fxmlName = getClass().getSimpleName() + ".fxml";
        return getClass().getResource("/com/example/eatti/fxml/" + fxmlName);
    }
}

然后让RecipeListPresenter继承这个BasePresenter,替换原有的FXMLView继承:

public class RecipeListPresenter extends BasePresenter {
    // 原有代码不变
}

方法三:调整FXML文件位置(不推荐)

将RecipeListPresenter.fxml移动到resources/com/example/eatti/view/recipe/目录下,与Presenter类同包,这样Afterburner.fx可自动找到文件,但会打乱当前目录结构,不推荐使用。

内容的提问来源于stack exchange,提问作者mopmop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:50:56