Упаковка функционала пакетов в единый jar-файл также может использоваться для распространения модулей Java. JAR-файл, который по сути представляет скомпилированный модуль, называется модульным JAR-файлом.
Для создания модульного JAR-файла аналогичным образом применяется утилита jar.
Если модуль содержит несколько пакетов, то также лучше всего его компилировать с опцией -d, которая помещает файлы классов в отдельный каталог (если каталог еще не существует, то он создается).
А при упаковке файлов в jar-файл для перехода в этот каталог применяется опция -C команды jar. Рассмотрим на примере.
Для начала где-нибудь на жестком диске (в моем случае это папка worlspace/java) создадим каталог, который назовем demo. Этот каталог будет представлять одноименный модуль. И в этом каталоге demo определим новый файл module-info.java - файл дескриптора модуля со следующим кодом:
module demo {
}
Далее в каталоге demo создадим папку com. В папке com создадим папку metanit, а в папке com/metanit - папку hello.
В папке com/metanit/hello определим новый файл Hello.java:
package com.metanit.hello;
public class Hello{
public static void main(String[] args){
System.out.println("Hello METANIT.COM!");
}
}
Название пакета файла - com.metanit.hello отражает структуру папок, в которых расположен файл. Сам файл определяет класс Hello, который в методе main выводит на консоль строку.
В итоге у нас получится следующая стуктура проекта:
Теперь скомпилируем все это и упакуем в файл jar.
Для создания jar перейдем в командной строке/терминале к папке, где находится папка demo с нашим модулем (в моем случае это папка worlspace/java). Затем для компиляции модуля выполним следующую команду:
javac --module-source-path . -d mods demo/module-info.java demo/com/metanit/hello/Hello.java
После компиляции модуля в текущей папке появится папка "mods", в которую будут помещаться все скомпилированные классы модуля demo и которая будет иметь следующую структуру:
mods
|_demo
|_module-info.class
|_com
|_metanit
|_hello
|_Hello.class
Далее для упаковки в jar выполним следующую команду:
jar -c -v -f demo.jar -C mods/demo .
Команда принимает следующие параметры
-c: указывает на создание нового архива.
-v: предоставляет подробный вывод выполнения команды
-f demo.jar: задает имя создаваемого JAR-файла. То есть создаваемый файл будет называться "demo.jar"
-C mods/demo .: переходит в папку с скомпилированными классами (mods/имя__модуля) и добавляет все ее содержимое (.) в JAR. В данном случае это папка "mods" (название произвольное) и ее подкаталог "demo", название которого должно соответствовать имени модуля, указананного в module-info.java (если указано). В примере выше модуль называется "demo", соответственно все компилируемые файлы помещаются в папку "mods/demo"
Полный консольный вывод:
eugene@Eugene:~$ cd workspace/java eugene@Eugene:/workspace/java$ javac --module-source-path . -d mods demo/module-info.java demo/com/metanit/hello/Hello.java eugene@Eugene:/workspace/java$ jar -c -v -f demo.jar -C mods/demo . added manifest added module-info: module-info.class adding: com/(in = 0) (out= 0)(stored 0%) adding: com/metanit/(in = 0) (out= 0)(stored 0%) adding: com/metanit/hello/(in = 0) (out= 0)(stored 0%) adding: com/metanit/hello/Hello.class(in = 440) (out= 305)(deflated 30%) eugene@Eugene:/workspace/java$
Но в примере выше класс Hello является исполняемым, то есть он имеет метод main() и может быть запущен на выполнение. И
как и в обычных JAR-файлах, в модульном JAR-файле можно указать основной класс, который будет выполняться при запуске.
Для этого предназначена опция -e (полное название опции - --main-class), которой передается название основного класса с учетом его пакетов:
jar -c -v -f demo.jar -e com.metanit.hello.Hello -C mods/demo .
В примере выше класс Hello определен в пакете com.metanit.hello, поэтому указываем в качестве полного имени файла "com.metanit.hello.Hello".
После этого можно запустить созданный модульный JAR-файл с помощью команды java:
java --module-path demo.jar --module demo
Полный консольный вывод:
eugene@Eugene:/workspace/java$ javac --module-source-path . -d mods demo/module-info.java demo/com/metanit/hello/Hello.java eugene@Eugene:/workspace/java$ jar -c -v -f demo.jar -e com.metanit.hello.Hello -C mods/demo . added manifest added module-info: module-info.class adding: com/(in = 0) (out= 0)(stored 0%) adding: com/metanit/(in = 0) (out= 0)(stored 0%) adding: com/metanit/hello/(in = 0) (out= 0)(stored 0%) adding: com/metanit/hello/Hello.class(in = 440) (out= 305)(deflated 30%) eugene@Eugene:/workspace/java$ java --module-path demo.jar --module demo Hello METANIT.COM! eugene@Eugene:/workspace/java$
Рассмотрим другую, более сложную ситуацию, где у нас есть несколько модулей, которые взаимодействуют друг с другом. Допустим, у нас есть следующая структура проектов:
Здесь определено два модуля: demo и operations. Каждый располагается в своей одноименной папке.
В папке operations/com/metanit/factorial определен следующий файл Factorial.java:
package com.metanit.factorial;
public class Factorial{
public static int calculate(int n){
if (n < 1) return -1;
int result = 1;
while (n > 1) result *= n--;
return result;
}
}
Класс Factorial определяет функцию вычисления факториала и принадлежит пакету com.metanit.factorial.
Чтобы сделать этот класс и в целом его пакет доступным для использования в других модулях в папке operations определен следующий файл module-info.java:
module operations {
exports com.metanit.factorial;
}
В папке demo определен следующий файл module-info.java:
module demo {
requires operations;
}
С помощью оператора requires< указываем, что модуль demo будет использовать модуль operations.
А в папке demo/com/metanit/hello/ определим следующий файл Hello.java:
package com.metanit.hello;
import com.metanit.factorial.Factorial;
public class Hello{
public static void main(String[] args){
int a = 5;
int b = Factorial.calculate(a);
System.out.printf("Factorial of %d is equal to %d \n", a, b);
}
}
Здесь мы подключаем класс Factorial из пакета "com.metanit/factorial" и вычисляем факториал числа.
Теперь нам надо оба модуля скомпилировать в jar-файлы. Стандартный подход предполагает, что оба модуля компилируются по отдельности каждый в свой jar-файл. В консоли перейдем к корневой папке, где размещаются оба модуля (в моем случае это папка "/workspace/java") и скомпилируем их. Для этого выполним следующую команду:
javac --module-source-path . -d mods demo/module-info.java demo/com/metanit/hello/Hello.java
Хотя здесь указан только один модуль, зависимый модуль также будет скомпилирован. Оба модуля в данном случае будут помещаться в папку "mods", которая в итоге будет иметь следующую структуру:
mods
|_demo
| |_module-info.class
| |_com
| |_metanit
| |_hello
| |_Hello.class
|_factorial
|_module-info.class
|_com
|_metanit
|_factorial
|_Factorial.class
Далее скомпилируем модуль operations:
jar -c -v -f libs/operations.jar -C mods/operations .
Компилируем из модуля файл "operations.jar" будет помещаться в папку "libs". Далее скомпилируем модуль demo:
jar -c -v -f libs/demo.jar -e com.metanit.hello.Hello -C mods/demo .
В итоге в папке "libs" будут располагаться оба скомпилированных модуля. Теперь запустим главный модуль, передав путь к папке с модульными jar:
java --module-path libs --module demo/com.metanit.hello.Hello
Полный консольный вывод:
eugene@Eugene:/workspace/java$ javac --module-source-path . -d mods demo/module-info.java demo/com/metanit/hello/Hello.java eugene@Eugene:/workspace/java$ jar -c -v -f libs/operations.jar -C mods/operations . added manifest added module-info: module-info.class adding: com/(in = 0) (out= 0)(stored 0%) adding: com/metanit/(in = 0) (out= 0)(stored 0%) adding: com/metanit/factorial/(in = 0) (out= 0)(stored 0%) adding: com/metanit/factorial/Factorial.class(in = 339) (out= 266)(deflated 21%) eugene@Eugene:/workspace/java$ jar -c -v -f libs/demo.jar -e com.metanit.hello.Hello -C mods/demo . added manifest added module-info: module-info.class adding: com/(in = 0) (out= 0)(stored 0%) adding: com/metanit/(in = 0) (out= 0)(stored 0%) adding: com/metanit/hello/(in = 0) (out= 0)(stored 0%) adding: com/metanit/hello/Hello.class(in = 660) (out= 421)(deflated 36%) eugene@Eugene:/workspace/java$ java --module-path libs --module demo/com.metanit.hello.Hello Factorial of 5 is equal to 120 eugene@Eugene:/workspace/java$