在 javafx 和 hibernate 中使用 guice

using guice with javafx and hibernate

我有一个 javafx 控制器,看起来像这样

public class DictionaryFxController implements Initializable {

    @Inject
    private QueryTemplate queryTemplate;

    @SuppressWarnings("unchecked")
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ....
        queryTemplate.insert(langs);
        ....
    }
}

我的applicationclass看起来像这样

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Injector injector = Guice.createInjector(new ApplicationModule());
        Scene scene = null;
        FXMLLoader loader = new FXMLLoader();
        try {
            Parent root = loader.load(getClass().getResourceAsStream("/dictionary.fxml"));
            loader.setControllerFactory(injector::getInstance);
            scene = new Scene(root);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }

        primaryStage.setTitle("Dictionary");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

}

我的 class 提供 Hibernate 会话看起来像这样

@Singleton
public class HibernateController {
    private SessionFactory sessionFactory;

    public HibernateController() {
        sessionFactory = new Configuration()
                .configure()
                .buildSessionFactory();
    }

    public Session openSession() {
        return sessionFactory.openSession();
    }

    @PreDestroy
    private void close() {
        sessionFactory.close();
        StandardServiceRegistryBuilder.destroy(sessionFactory.getSessionFactoryOptions().getServiceRegistry());
    }
}

我将这个 class 连接到我用来执行查询的 template

public class QueryTemplate {
    private HibernateController hibernateController;

    @Inject
    public QueryTemplate(HibernateController controller) {
        this.hibernateController = controller;
    }

    public <T> void insert(List<T> objects) {
        try (Session session = hibernateController.openSession()) {
            Transaction tx = session.beginTransaction();

            // generalize
            objects.forEach(session::save);

            tx.commit();
        }
    }
}

我在模块

中绑定了这个class
public class ApplicationModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(QueryTemplate.class);
    }
}

但是当我启动应用程序时,在线上失败

   queryTemplate.insert(langs);

因为 queryTemplate 为空。 有什么问题?

您正在设置控制器工厂加载 FXML 之后;此时控制器已经创建。由于 FXMLLoader 将使用默认机制来创建控制器 - 即简单地调用控制器的默认构造函数 - 控制器不受 Guice 管理,并且不会初始化其注入字段。

您只需颠倒 loader.setControllerFactory(...)loader.load(...) 行的顺序即可解决问题。

顺便说一句,设置 FXMLLoader 的位置比使用 load(InputStream) 方法更好。如果您使用获取 Stream 的方法,则未设置位置,并且位置解析将无法在您的 FXML 文件中正常工作。

所以替换

    FXMLLoader loader = new FXMLLoader();
    try {
        Parent root = loader.load(getClass().getResourceAsStream("/dictionary.fxml"));
        loader.setControllerFactory(injector::getInstance);
        scene = new Scene(root);
    } catch (IOException e) {
        throw new IllegalStateException(e);
    }

    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/dictionary.fxml"));
    loader.setControllerFactory(injector::getInstance);
    try {
        Parent root = loader.load();
        scene = new Scene(root);
    } catch (IOException e) {
        throw new IllegalStateException(e);
    }

您可以将前两行合并为一行

    FXMLLoader loader = new FXMLLoader(getClass().getResource("/dictionary.fxml"));

如果你愿意。