Я создал два проекта Maven «javafx» и «commonfx», чтобы воспроизвести ошибку.
module commonfx {
exports commonfx.calculator;
}
module javafx {
requires javafx.controls;
requires javafx.graphics;
requires javafx.fxml;
requires commonfx;
opens org.example to javafx.graphics, javafx.fxml;
}
Оба проекта имеют папку ресурсов. Они не экспортируют эти папки, но в любом случае я постоянно получаю сообщения об ошибках:
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException:
Package bundle in both module javafx and module commonfx
Всякий раз, когда я перезапускаю приложение, я получаю новое сообщение об ошибке. Но каждый раз папка ресурсов имеет одно и то же имя в обоих модулях.
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException:
Package images in both module commonfx and module javafx
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException:
Package main.resources.images in both module commonfx and module javafx.
Используемые технологии:
Возможно ли, что папка ресурсов maven игнорируется?
Код для воспроизведения исключения:
Проект commonfx
package commonfx.calculator;
public class FxCalculator {
public FxCalculator() {
super();
}
public Integer calculate() {
return 2;
}
}
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>common.fx</groupId>
<artifactId>commonfx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>21</javafx.version>
<javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.maven.plugin.version}</version>
</plugin>
</plugins>
</build>
</project>
Проект javafx
package org.example;
import commonfx.calculator.FxCalculator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Javafx extends Application {
private FxCalculator calculator;
@Override
public void start(Stage stage) {
String javaVersion = System.getProperty("java.version");
String javafxVersion = System.getProperty("javafx.version");
calculator = new FxCalculator();
Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "." + " Number FX: " + calculator.calculate());
Scene scene = new Scene(new StackPane(l), 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>javafx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>21</javafx.version>
<javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>net.rgielen</groupId>
<artifactId>javafx-weaver-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>common.fx</groupId>
<artifactId>commonfx</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.maven.plugin.version}</version>
<configuration>
<mainClass>org.example.Javafx</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Ага. Это даже не пакет, и он не экспортируется. Есть идеи, как это решить? Может быть, удалить из пути сборки?
Ваш пакет в commonfx — commonfx.calculator. Почему бы не разместить для этого ресурсы src/main/resources/commonfx/calculator? Затем они правильно инкапсулируются в модуль. Согласно спецификации модуля : «Модуль — это именованная, самоописывающая коллекция кода и данных. Его код организован как набор пакетов, содержащих типы, т. е. классы и интерфейсы Java; его данные включают ресурсы и другие виды статической информации.».
Если вы хотите дополнительно разбить его по типам ресурсов, как вы это делаете сейчас, тогда src/main/resources/commonfx/calculator/bundle и src/main/resources/commonfx/calculator/images, с exports commonfx.calculator.bundle; и exports commonfx.calculator.images; в определении вашего модуля. Я не пробовал, но должно сработать.
Папки, экспортированные из вашей исходной иерархии в вашу сборку, становятся пакетами в сборке, независимо от того, содержат ли они файлы классов или ресурсы. Ресурсы объединяются в пакеты, и доступ к ним осуществляется через содержащие их пакеты, как и классы. Экспортируемые пакеты должны иметь уникальные имена (по возможности следует использовать обратные доменные имена). Как минимум, вы должны дать пакетам уникальные имена (опять же, независимо от того, содержат ли они классы или ресурсы).
Кстати, не только имена пакетов, но и имена модулей должны быть уникальными, например. org.example.my_product.app вместо javafx.
@jewelsea Я добавил код для воспроизведения. Ваш подход «сначала уникальное переименование пакетов ресурсов, а затем их экспорт» не работает. У вас это работает?
Да, предложенный мной подход работает для меня. Я добавил ответ, демонстрирующий этот подход.




TL;DR: Это проблема разделенного пакета. Решение состоит в том, чтобы либо:
Переименуйте свои пакеты, чтобы они были уникальными.
Переименуйте свои пакеты во что-то, что не является допустимым именем пакета (только вариант, если пакеты не содержат файлов классов).
Сделайте свой код немодульным и поместите свой код и его зависимости (кроме JavaFX) в путь к классам.
См. ответ Jewelsea, где приведен рабочий пример первого решения, а также причины, по которым третье решение, вероятно, является лучшим для вашего (задающего вопрос) конкретного сценария.
Ошибка, которую вы видите, связана с двумя модулями, содержащими один и тот же пакет. Это не то, что следует решать путем исключения папок ресурсов через Maven.
Во-первых, пакет не обязательно должен содержать файлы классов, чтобы считаться пакетом. На самом деле важно только то, что пакет имеет допустимое имя и что-то содержит, будь то файл класса или какой-либо другой ресурс. Кроме того, файлы классов — это, по сути, особый тип ресурсов. С точки зрения (типичного) загрузчика классов ресурсы и файлы классов находятся и открываются одинаково.
Другими словами, bundle, config и images в вашем случае — это пакеты. Это действительные имена пакетов, и они содержат ресурсы. Кроме того, поскольку это пакеты, содержащиеся в модуле, ресурсы внутри них инкапсулированы (функция, основанная на пакетах).
Когда два (или более) модуля содержат пакет с одинаковым именем, этот пакет называется «разделенным». Система модулей платформы Java гарантирует, что при наличии разделенного пакета два (или более) модуля не мешают друг другу. Это гарантируется на двух разных этапах процесса загрузки модулей.
Во время разрешения модуля разрешение может не удаться, если:
Два или более модулей в конфигурации экспортируют один и тот же пакет в модуль, который читает оба. Это включает в себя случай, когда модуль
M, содержащий пакетp, считывает другой модуль, который экспортируетpвM.
Затем определяется слой модуля 🔁, который может выйти из строя, если:
Два или более модуля одного и того же пакета сопоставляются с одним и тем же загрузчиком классов.
Ни bundle, ни images не экспортируются, поэтому ваши два модуля разрешаются нормально. Ошибка, которую вы видите, возникает при определении слоя, поэтому LayerInstantiationException является типом исключения. Ваши два модуля сопоставляются с одним и тем же загрузчиком классов (загрузчик классов системы/приложения).
По сути, разделенные пакеты полностью запрещены для модулей, загружаемых с помощью параметра командной строки --module-path. Ни график читаемости модуля, ни экспортирование разделенного пакета не имеют значения.
Также см. Перекрытие неэкспортируемых пакетов Java 9.
Есть как минимум три решения: использовать уникальные имена пакетов, использовать недопустимые имена пакетов или сделать код немодульным.
Вам следует использовать уникальные имена пакетов, даже если пакет не будет экспортироваться. Традиционный способ создания уникального имени пакета — это сочетание перевернутого доменного имени с именем, идентифицирующим артефакт. Эти две вещи обычно извлекаются из координат Maven указанного артефакта. Например, если у вас есть:
<groupId>com.example</groupId>
<artifactId>commonsfx</artifactId>
В вашем POM имя вашего пакета будет com.example.commonsfx. Это также было бы хорошим названием для вашего модуля.
Полный пример см. в ответе Jewelsea.
Если вы переименуете bundle и images во что-то, что не является допустимым именем пакета, они больше не будут считаться пакетами. Это позволит вам иметь один и тот же «пакет» в нескольких модулях, но это также будет означать, что ресурсы внутри этих «пакетов» больше не инкапсулируются.
Предупреждение. Это вариант только в том случае, если пакеты не содержат файлов классов.
Если вы сделаете свой код немодульным и поместите его в путь к классам, ваши разделенные пакеты больше не будут проблемой. Для вашего конкретного случая это, вероятно, лучшее решение. См. конец ответа Jewelsea, и вы узнаете, почему.
Пример модульного приложения, которое зависит от другого модуля, содержащего ресурсы для изображений и текста.
Приложение состоит из:
org.example.calculator, включающий основные функции и ресурсы для реализации калькулятора.org.example.calculatorapp, представляющий собой приложение JavaFX, функции и ресурсы которого зависят от модуля калькулятора.Использованы принципы, описанные в ответе Слоу и других комментариях к вопросу. Ресурсы инкапсулированы в те же пакеты, которые определены в каждом модуле, а пакеты, определенные в каждом модуле, имеют отдельные имена.
Это модуль, от которого зависит.
Все необходимые ресурсы инкапсулируются в модуле путем размещения их в том же месте пакета, что и остальная часть кода модуля, который затем экспортируется из модуля в файле Module-info.
pom.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>calculator</artifactId>
<version>1.0-SNAPSHOT</version>
<name>calculator</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/java/module-info.java
module org.example.calculator {
exports org.example.calculator;
}
src/main/java/org/example/calculator/Calculator.java
package org.example.calculator;
import java.util.Objects;
import java.util.ResourceBundle;
public class Calculator {
private final ResourceBundle resources = ResourceBundle.getBundle(
"org.example.calculator.calculator-resources"
);
public String add(int a, int b) {
return a + resources.getString("add") + b + resources.getString("equals") + (a+b);
}
public String subtract(int a, int b) {
return a + resources.getString("subtract") + b + resources.getString("equals") + (a-b);
}
public String getIconUrlString() {
return Objects.requireNonNull(
Calculator.class.getResource(
"calc.png"
)
).toExternalForm();
}
}
src/main/resources/org/example/calculator/calculator-resources.properties
add=+
subtract=-
equals==
src/main/resources/org/example/calculator/calc.png
pom.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>calculatorApp</artifactId>
<version>1.0-SNAPSHOT</version>
<name>calculatorApp</name>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.2</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>calculator</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/java/module-info.java
module org.example.calculatorapp {
requires javafx.controls;
requires org.example.calculator;
exports org.example.calculatorapp;
}
src/main/java/org/example/calculatorapp/CalculatorApp.java
package org.example.calculatorapp;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.example.calculator.Calculator;
public class CalculatorApp extends Application {
@Override
public void start(Stage stage) {
Calculator calculator = new Calculator();
Image calculatorImage = new Image(
calculator.getIconUrlString(),
64, 64, true, true, false
);
Label title = new Label("Calculator");
title.setStyle("-fx-font-size: 32px;");
title.setGraphic(new ImageView(calculatorImage));
GridPane grid = new GridPane(10, 10);
grid.setStyle("-fx-font-size: 20px;");
Label aLabel = new Label("a:");
TextField aField = new TextField("10");
grid.addRow(0, aLabel, aField);
Label bLabel = new Label("b:");
TextField bField = new TextField("2");
grid.addRow(1, bLabel, bField);
Button add = new Button("add");
Button subtract = new Button("subtract");
grid.addRow(2, add, subtract);
Label equationLabel = new Label("Equation:");
Label equationResult = new Label();
grid.addRow(3, equationLabel, equationResult);
add.setOnAction(e ->
equationResult.setText(
calculator.add(
Integer.parseInt(aField.getText()),
Integer.parseInt(bField.getText())
)
)
);
subtract.setOnAction(e ->
equationResult.setText(
calculator.subtract(
Integer.parseInt(aField.getText()),
Integer.parseInt(bField.getText())
)
)
);
VBox layout = new VBox(10, title, grid);
layout.setPadding(new Insets(12));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Рекомендация: для кода, содержащего автоматические модули.
Приведенный выше пример предназначен для полностью модульной системы, в коде которой есть четко определенные файлы module-info.java.
Ваш модуль проекта javafx в вашем вопросе не соответствует зависимостям, которые есть в вашем pom.xml.
Проект Maven включает зависимости Spring, но определение модуля не требует модулей Spring. Начиная с Spring 6, Spring использует автоматические модули. Многие преимущества модульного кода теряются при использовании автоматических модулей.
Итак, вместо того, чтобы использовать подход, описанный в этом ответе, сделайте проект немодульным, удалите информацию о модуле и поместите модули javafx в путь к модулю, используя параметры виртуальной машины.
Используя третий подход, мне еще нужно выяснить, как автоматически добавить библиотеку javax.smartcardio. Модуль smartcardio не является обязательным, но он все равно включен в комплектацию SE. Поэтому, если я хочу его использовать, мне нужно указать его в модуле-info.java. В противном случае мне нужно будет добавить его в проект вручную после каждого обновления Maven. Есть идеи, как решить эту проблему?
Что такое «третий подход»? javax.smartcardio -> Это программное обеспечение не упомянуто в вашем вопросе. Возможно, задайте новый вопрос с минимально воспроизводимым примером специально для этого. «Мне нужно это потребовать в модуле-info.java» -> Вероятно, это заблуждение. Написано очень мало программного обеспечения Java, которое абсолютно требует, чтобы оно работало как модуль. «Мне нужно добавлять его в проект вручную после каждого обновления Maven» -> это тоже кажется заблуждением.
@Biilie Если ваше приложение немодульное: если javax.smartcardio не разрешается, вы можете добавить его через --add-modules. Если это не помогает или вам нужна дополнительная помощь, я поддерживаю рекомендацию задать новый вопрос с минимально воспроизводимым примером.
@jewelsea Я создала новый пост: ссылка
Интересно узнать, действительно ли
bundleявляется посылкой?