null

Курим сборку jar

Завариваем кофеек... 100%

Усаживаемся поудобнее... 100%

 

Приветствую! На связи Жабка с кофе laugh

Сегодня речь пойдет о "ручной" сборке jar при помощи стандартных утилит из состава JDK.

Вы узнаете, что такое:

  • архив jar
  • утилита jar
  • утилита javac
  • утилита java
  • сlasspath
  • манифест
  • исполняемый jar архив

 

Байт-код и компиляция

Когда речь идет о Java, всем приходит на ум "кроссплатформенность". Создатели языка решили проблему запуска на разных платформам при помощи создания виртуальной Java машины (JVM). Ей на вход передается скомпилированный байт-код, а на выходе мы получаем работающую программу. Причем скомпилировать нашу программу требуется всего 1 раз и полученный байт-код может быть запущен на любой машине (в теории), на которой стоит JVM.

Чтобы скомпилировать исходный код, мы используем утилиту "javac", которая есть в любой поставке JDK. 

Предположим, мы имеем следующее дерево директорий в нашем проекте:

- build

----- classes

----- libs

----- meta

- libs

----- some-lib.jar

src

----- org/example/jar

------------ somePackageA

------------ somePackageB 

------------ HelloWorld.java

Здесь в директории "src" мы храним исходный код нашей программы. Код в Java  программе принято организовывать в пакеты. Пакеты выполняют несколько функций. Во-первых, они позволяет "разложить по папочкам" ваш код - как результат, становится проще воспринимать содержимое проекта. Во-вторых, они позволяют изолировать одни классы от других (при помощи модификаторов доступа, используемых в классах) - как результат, ваш товарищ по несчастью разработчик не сможет использовать те части кода, которые ему использовать не нужно. Как говорится, чем меньше возможностей выстрелить себе в ногу - тем лучше! Однако важно помнить, что структура пакетов должна в точности соответствовать структуре дерева директорий, иначе JVM не сможет найти необходимый ей пакет.

Раз уж речь зашла о пакетах, нельзя не упоминуть о "полном имени класса" и о "пакете по умолчанию".

  • полное имя класса - названия пакетов, в которых лежит класс, разделенные точкой + само имя класса. Например, в примере полное имя класса "HelloWorld" - "org.example.jar.HelloWorld"
  • пакет по умолчанию - если некоторый класс не лежит ни в одном пакете, то считается, что он лежит в пакете по умолчанию. Причем у этого пакета нет имени.

Оставшиеся директории:

  • build - директория, в которой расположены байт-код, файлы манифестов и результирующие jar архивы.
  • libs - директория, в которой расположены скачанные зависимости.

Рассмотрим теперь команду, которая позволит нам скомпилировать код из примера:

cd PROJECT_ROOT
javac -d build/classes -cp libs/some-lib.jar src/org/example/jar/*.java src/org/example/jar/**/*.java

Команда javac имеет вид "javac <options> <source files>" . Из ключей наиболее полезны два: -d указывает, куда поместить итоговой байт-код (причем команда javac автоматически создает надлежащую иерархие директорий для пакетов);  -cp указывает classpath (о нем слегка позднее). В качестве <source files> можно указывать не только конкретные имена файлов, но также маски.

 

Так что же такое classpath?

Часто мы хотим использовать какие-либо библиотеки\фреймоворки при написании своего кода. Jar архивы, которые содержат код вне нашего проекта, принято называть зависимостями. При компиляции Java компилятор проверяет код на адекватность, например, проверяет импорты. Поэтому компилятору необходимо знать, где искать зависимости. В этом компилятору поможет classpath. В прицнипе, classpath это всего лишь набор путей до директории, jar и zip архивов, разделенный двоеточием (для ОС Windows разделитель - точка с запятой). Мы можем указать classpath для таких утилит как: java, javac - при помощи ключа "-cp".

 

Что такое jar архив?

Jar архив это архив, содержащий Java байт-код и метаинформацию о нем. Метаинформация хранится в специальном файле MANIFEST.MF, расположенном в директории META-INF внутри jar архива. В манифесте можно указать такую информацию как: версия, информация о разработчике, точка входа в программу и даже classpath.  Когда в манифесте jar архива содержится указание на точку входа в программу (т.е. полное имя класса, содержащего метод main), его называют исполняемым jar-архивом. Очевидно, потому что он может быть "запущен".

Создадим файл MANIFEST.MF в директории build/meta со следующим содержимым (предполагая, что в классе org.example.jar.HelloWorld есть метод main):

Main-Class: org.example.jar.HelloWolrd

Атрибут Main-Class внутри манифеста - это указание полного имени класса, содержащего в себе точку входа.

Теперь, чтобы собрать jar архив используем утилиту "jar":

cd PROJECT_ROOT
jar -cvfm build/libs/example.jar build/meta/MANIFEST.MF -C build/classes . 

Используемые ключи:

  • c - ключ, который указывает утилите, что необходимо создать новый архив
  • v - ключ, который указывает утилите, подробно описывать результат своей работы
  • f - ключ, который указывает утилите, что ей будет передано имя создаваемого jar архива
  • m - ключ, который указывает утилите, что ей будет передано имя файла манифеста. Причем относительный порядок ключей f и m определяет порядок в котором будут переданы имена архива и манифеста. К слову, в качестве имен архива и манифеста могут также выступать и пути.
  • C - ключ, который указывает утилите, относительно какой директории разрешать имена файлов. То есть, например, если байт-код лежит по пути build/classes, мы можем указать этот путь при помощи ключа -C и тогда в качестве последнего аргумента команды, который принимает пути до файлов, включаемые в результирующий архив, можно просто указать точку.

Оп и jar архив готов!

 

Запуск исполняемого jar архива

Теперь мы можем запустить наш исполняемый архив и радоваться жизни:

cd PROJECT_ROOT
java -cp libs/some-lib.jar -jar build/libs/example.jar

Здесь при помощи ключа "-cp" мы указываем зависимости, а при помощи ключа "-jar" указываем, что аргумент команды - это исполняемый jar архив. Вообще говоря, если не указывать classpath вручную, JVM получит "дефолтный classpath" в виде директории, из которой была выполнена команда запуска. Кроме того, сам запускаемый jar всегда является classpath-ом. То есть, если поместить содержимое some-lib.jar прямо внутрь нашего jar архива example.jar, можно не указывать вручную classpath. Это сборка, так называемого "толстого jar".

 

Пример проекта для тестирования сборки jar архива, можете найти здесь:  репозиторий GitHub

 

На этом у жабки кончилось кофе laugh

До новых встреч!

Назад