Что такое исполняемый файл в java
Вопрос такой что такое jar файл и зачем он нужен на практике. Ведь если есть файл java, есть файл class который уже переведен в байт-код, зачем тогда jar файл?
Какая его функция и можно ли без него обойтись?
Резюме
Начиная с Java SE 11 и впервые в истории программирования вы можете напрямую исполнять скрипты с Java-кодом без компилирования. Это позволяет писать скрипты на Java и исполнять их из *nix-командной строки.
Сейчас уже никто не создает программы в консоли. Используя любимую IDE, разработчик чувствует себя неуютно за чужим компьютером, где её нет.
Решив разобраться в работе Ant и Maven, я поймал себя на том, что не смогу собрать приложение без них в консоли.
В данной статье я постарался уместить все этапы проектирования демонстрационного приложения, чтобы не искать справку по каждой команде на просторах Интернета.
Можно ли передавать в командной строке аргументы?
Давайте расширим нашу программу Hello Universe, чтобы она выводила персональное приветствие любому пользователю, зашедшему на InfoQ Universe:
Сохраним код в файле Greater.java. Обратите внимание, что имя файла не соответствует имени публичного класса. Это нарушает правила спецификации Java.
Как видите, совершенно не важно, что имена класса и файла не совпадают. Внимательный читатель мог также заметить, что мы передали в код аргументы после обработки имени файла. Это означает, что любой аргумент в командной строке, идущий после имени файла, передаётся стандартному основному методу.
Плагин Apache Shade
Плагин Assembly является общим; плагин Shade ориентирован исключительно на задачу создания самодостаточных JAR.
Этот плагин предоставляет возможность упаковать артефакт в uber-jar, включая его зависимости, и оттенить — т.е. переименовать — пакеты некоторых зависимостей.
— Плагин Apache Maven Shade
Плагин основан на концепции преобразователей: каждый преобразователь отвечает за работу с одним типом ресурсов. Преобразователь может копировать ресурс как есть, добавлять статическое содержимое, объединять его с другими и т.д.
Хотя вы можете разработать свой преобразователь, плагин предоставляет набор готовых преобразователей:
Конфигурация плагина Shade к приведенному выше Assembly выглядит следующим образом:
shade привязан к фазе package по умолчанию
Этот преобразователь предназначен для генерации файлов манифеста
Выполните ввод Main-Class
Настройте финальный JAR так, чтобы он был многорелизным JAR. Это необходимо в случае, когда любой из исходных JAR является многорелизным JAR
Запуск mvn package дает два артефакта:
-.jar : самодостаточный исполняемый JAR
original--.jar : "обычный" JAR без встроенных зависимостей
При работе с проектом, взятым за образец, финальный исполняемый файл все еще не работает так, как ожидалось. Действительно, во время сборки появляется множество предупреждений о дублировании ресурсов. Два из них мешают корректной работе проекта. Чтобы правильно их объединить, нам нужно посмотреть на их формат:
META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat: этот Log4J2 файл содержит предварительно скомпилированные данные плагина Log4J2. Он закодирован в двоичном формате, и ни один из готовых преобразователей не может объединить такие файлы. Тем не менее, случайный поиск показывает, что кто-то уже занимался этой проблемой и выпустил преобразователь для работы с объединением.
META-INF/spring.factories : эти файлы, специфичные для Spring, они имеют формат "один ключ/много значений". Поскольку они текстовые, ни один готовый преобразователь не может корректно объединить их. Однако разработчики Spring предоставляют такую возможность (и многое другое) в своем плагине.
Чтобы настроить эти преобразователи, нам нужно добавить вышеуказанные библиотеки в качестве зависимостей к плагину Shade:
Объедините Log4J2 .dat файлы
Объедините файлы /META-INF/spring.factories
Добавьте необходимый код для преобразователей
Эта конфигурация работает! Тем не менее, есть оставшиеся предупреждения:
Лицензии, предупреждения и схожие файлы
Spring Boot файлы, например, spring.handlers , spring.schemas и spring.tooling
Файлы Spring Boot-Kotlin, например, spring-boot.kotlin_module , spring-context.kotlin_module , и так далее.
Файлы конфигурации Service Loader
Вы можете добавить и настроить дополнительные преобразователи для устранения вышеупомянутых пунктов. В целом, весь процесс требует глубокого понимания каждого вида ресурсов и знаний от том, как с ними работать.
Советы
- Некоторые опции, которые вы будете передавать в javac, могут не передаться (или не распознаться) java , например, опции -processor или -Werror .
- Если в classpath есть файлы .class и .java, то модуль запуска заставит вас использовать class-файл.
Обратите внимание на два файла java.java в пакете HelloUniverse и файл HelloUniverse.java в той же директории. Если вы попробуете выполнить:
Можно подписать jar-архив
Если требуется подписать свою библиотеку цифровой подписью, на помощь придут keytool и jarsigner.
Генерируем подпись.
Генерируем Certificate Signing Request (CSR)
Содержимое полученного файла отправляем в центр сертификации. От центра сертификации получаем сертификат. Сохраняем его в файле (например, qwertokey.cer) и импортируем в хранилище
Файл qwertokey.cer отправляем всем, кто хочет проверить архив. Проверяется он так
Лучше снабдить библиотеку документацией
Изменим для этого класс калькулятора.
Документацию можно создать следующей командой. При ошибке программа выдаст список возможных опций.
В результате получиться следующее
Создадим библиотеку
Класс Calculator оказался полезным и может быть использован во многих проектах. Перенесем всё, что касается класса Calculator в отдельный проект.
Измените также назавания пакетов в исходных текстах. В HelloWorld.java нужно будет добавить строку
Делаем архив jar
С помощью ключа -C мы запустили программу в каталоге bin.
1 ответ 1
JAR-файл — это Java-архив (Java ARchive). Это простой архивный файл, сжатый (иногда с нулевой компрессией) по алгоритму zip.
Он был создан для удобства распространения программ, написанных на Java. Так как обычная программа содержит сотни, тысячи, а иногда и миллионы файлов. Файл может содержать:
- файл манифеста META-INF/MANIFEST.MF
- java-файлы (исходный код)
- class-файлы
- файлы, необходимые для работы программы: картинки, файлы с настройками и прочее (ресурсы)
- электронные подписи, которые позволяют защитить программу от модификации
Манифест - это текстовый файл формата ключ: значение ; он содержит описание jar-файла. В нем могут быть следующие ключи:
- Manifest-Version - версия манифеста
- Main-Class - имя главного класса (должен содержать метод main ), такой jar-файл можно запустить как обычный исполняемый файл
- Class-Path - позволяет указать CLASSPATH , который необходим для полноценной работы программы
- SHA-Digest - контрольная сумма определенного файла внутри архива
Кроме jar, также существуют другие архивы, связанные с Java:
- WAR (Web Application aRchive) - содержит в себе приложение для веба
- EAR (Enterprise Application aRchive) - содержит в себе энтерпрайз приложение (обычно из нескольких модулей)
- APK (Android aPplication pacKage) - содержит в себе приложение для Android
Можно ли обойтись без?
В принципе, можно, все что вам необходимо - это всегда точно копировать структуру вашего приложения. То есть все класс-файлы и файлы с ресурсами должны располагаться в правильных директориях. Переменная CLASSPATH должна содержать пути ко всем зависимостям. Если эти условия соблюдены, то ваше приложение будет работать без упаковки в jar.
P.S. В переводе с английского, jar - означает банка, стеклянный или керамический цилиндрический контейнер с широким горлом, предназначенный для хранения еды (обычно). Кофе часто хранят в банках, так что тут игра слов: банка с кофе - банка с java-классами.
Когда ваше программное приложение выходит за пределы десятка строк кода, вам, вероятно, следует разделить код на несколько классов. На этом этапе встает вопрос о том, как их распределить. В Java классическим форматом является Java-архив, более известный как JAR. Но реальные программы, вероятно, зависят от других JAR.
Цель этой статьи - описать способы создания самодостаточных исполняемых (self-contained executable) JAR, также известных как uber-JAR или fat JAR.
Shebang-файлы: запускаем Java как shell-скрипт
Итак, в Java SE 11 появилась поддержка скриптов, включая традиционные shebang-файлы из мира *nix. Для их поддержки не потребовалось спецификации языка.
Запустим следующий пример в терминале, работающем под macOS Mojave 10.14.5. Но сначала определим важные правила, которым нужно следовать при создании shebang-файла:
Сохраним код в файл с именем dirlist без расширения, а затем пометим его как исполняемый: mohamed_taman:code$ chmod +x dirlist .
Запустим снова с помощью команды, которая передаёт родительскую директорию, и проверим результат.
Примечание: при оценке исходного кода интерпретатор игнорирует shebang-строку (первую строку). Таким образом, shebang-файл можно явно вызвать с помощью модуля запуска, например, с дополнительными опциями:
Также нужно отметить: если скриптовый файл лежит в текущей директории, то вы можете выполнить его так:
А если скрипт лежит в директории, путь которой указан в пользовательском PATH, то выполнить его можно так:
И в завершение дам несколько советов, о чём нужно помнить при использовании скриптов.
Первый пример
Начнём с классического простейшего примера — Hello Universe!
Мы продемонстрируем описываемую возможность на разных примерах, чтобы вы получили представление, как можно её использовать в повседневном программировании.
Создайте файл HelloUniverse.java с кодом из начала статьи, скомпилируйте и запустите получившийся class-файл. Затем удалите его, сейчас поймёте зачем:
Если теперь с помощью Java-интерпретатора вы запустите class-файл без компиляции:
то увидите тот же результат: файл будет исполнен.
То есть под капотом всё же выполняется компиляция. И в случае её ошибки мы получим уведомление об этом. Можете проверить структуру директорий и убедиться, что class-файл не генерируется, компиляция выполняется в памяти.
Теперь давайте разберёмся, как это всё устроено.
Что вам потребуется
Для запуска кода, приведённого в статье, вам понадобится версия Java не ниже 11. На момент написания статьи текущим релизом был Java SE Development Kit 12.0.1 — финальная версия находится здесь, достаточно принять условия лицензии и кликнуть на ссылку для вашей ОС. Если хотите поэкспериментировать с самыми свежими возможностями, то можете скачать JDK 13 early access.
Обратите внимание, что сейчас также доступны релизы OpenJDK разных вендоров, в том числе AdoptOpenJDK.
В этой статье мы будем вместо Java IDE применять обычный текстовый редактор, чтобы избежать всякой магии IDE, и использовать командную строку Java прямо в терминале.
Плагин Spring Boot
Плагин Spring Boot использует совершенно другой подход. Он не объединяет ресурсы из JAR по отдельности; он добавляет зависимые JAR по мере их появления в uber JAR. Для загрузки классов и ресурсов он предоставляет специальный механизм. Очевидно, что он предназначен для проектов Spring Boot.
Настройка плагина Spring Boot проста:
Давайте проверим структуру финального JAR:
Скомпилированные классы проекта
Загрузка классов в Spring Boot
Вот выдержка из манифеста по образцу проекта:
Как вы можете видеть, главный класс является специфичным классом Spring Boot, в то время как "настоящий" главный класс упоминается в другой записи.
Для получения дополнительной информации о структуре JAR, пожалуйста, ознакомьтесь со справочной документацией.
Почему скрипты так важны в Java?
Сначала давайте вспомним, что такое скрипты:
Скрипт — это программа, написанная для определённого runtime-окружения, которая автоматизирует исполнение задач или команд, которые человек может исполнять поочерёдно.
Из этого общего определения мы можем вывести простое определение скриптового языка — это язык программирования, использующий высокоуровневые конструкции для интерпретации и исполнения по одной команде (или команд) за раз.
Скриптовый язык использует серии команд, записанных в файле. Часто такие языки являются интерпретируемыми (а не компилируемыми) и придерживающимися процедурного стиля программирования (хотя некоторые скриптовые языки также обладают свойствами объектно-ориентированных языков).
В целом скриптовые языки легче в освоении и быстрее в наборе кода по сравнению с более структурированными компилируемыми языками вроде Java, C и С++. К серверным скриптовым языкам относятся Perl, PHP и Python, а на клиентской стороне — JavaScript.
Долгое время Java считался хорошо структурированным, сильно типизированным компилируемым языком, который интерпретируется виртуальной машиной для выполнения на любой вычислительной архитектуре. Однако Java не так прост в изучении и прототипировании по сравнению с другими скриптовыми языками.
Тем не менее, Java уже исполнилось 24 года, его использует около 10 млн разработчиков по всему миру. В последних релизах добавили ряд новых возможностей, чтобы молодым программистам было легче изучать этот язык, а также чтобы пользоваться функциями языка и API без компилирования и IDE. Например, в Java SE 9 появился инструмент JShell (REPL), который поддерживает интерактивное программирование.
А с выходом JDK 11 этот язык получил возможность поддержки скриптов, поскольку теперь вы можете исполнять код с помощью простого вызова команды java !
В Java 11 есть два основных способа использования скриптов:
- Прямой вызов команды java .
- Применение *nix-скриптов для командной строки, аналогичных Bash-скриптам.
Можно использовать модули?
Да, никаких ограничений. Скомпилированный в памяти код запускается как часть безымянного модуля с опцией --add-modules=ALL-DEFAULT , которая даёт доступ ко всем модулям, поставляемым с JDK.
То есть код может использовать разные модули без необходимости явно определять зависимости с помощью module-info.java.
Запустим программу и получим результат:
Теперь вы можете быстро тестировать новые фичи, предоставляемые разными модулями, не создавая свой собственный модуль.
От простого к .
Каждая программа обычно содержится в отдельном каталоге. Я придерживаюсь правила создавать в этом каталоге по крайней мере две папки: src и bin. В первой содержатся исходные коды, во второй — результат компиляции. В данных папках будет структура каталогов, зависящая от пакетов.
Один файл
Можно сделать и без лишних папок.
Берем сам файл HelloWorld.java.
Переходим в каталог, где лежит данный файл, и выполняем команды.
В данной папке появится файл HelloWorld.class. Значит программа скомпилирована. Чтобы запустить
Отделяем бинарные файлы от исходников
Теперь сделаем тоже самое, но с каталогами. Создадим каталог HelloWorld и в нем две папки src и bin.
Компилируем
Здесь мы указали, что бинарные файлы будут сохраняться в отдельную папку bin и не путаться с исходниками.
Используем пакеты
А то, вдруг, программа перестанет быть просто HelloWorld-ом. Пакетам лучше давать понятное и уникальное имя. Это позволит добавить данную программу в другой проект без конфликта имен. Прочитав некоторые статьи, можно подумать, что для имени пакета обязательно нужен домен. Это не так. Домены — это удобный способ добиться уникальности. Если своего домена нет, воспользуйтесь аккаунтом на сайте (например, ru.habrahabr.mylogin). Он будет уникальным. Учтите, что имена пакетов должны быть в нижнем регистре. И избегайте использования спецсимволов. Проблемы возникают из-за разных платформ и файловых систем.
Поместим наш класс в пакет с именем com.qwertovsky.helloworld. Для этого добавим в начало файла строчку
В каталоге src создадим дополнительные каталоги, чтобы путь к файлу выглядел так: src/com/qwertovsky/helloworld/HelloWorld.java.
Компилируем
В каталоге bin автоматически создастся структура каталогов как и в src.
Если в программе несколько файлов
HelloWorld.java
Adder.java
Ошибка возникла из-за того, что для компиляции нужны файлы с исходными кодами классов, которые используются (класс Calculator). Надо указать компилятору каталог с файлами с помощью ключа -sourcepath.
Компилируем
Запускаем .java с помощью Java
Функция JEP 330 (запуск однофайловых программ с исходным кодом) появилась в JDK 11. Она позволяет напрямую исполнять исходные файлы с исходным Java-кодом, без использования интерпретатора. Исходный код компилируется в памяти, а затем исполняется интерпретатором без создания на диске .class-файла.
Однако эта функция ограничена кодом, который хранится в одном файле. Вы не можете исполнять сразу несколько исходных файлов.
Чтобы обойти это ограничение, все классы нужно определять в одном файле. Никаких ограничений на их количество нет. Кроме того, пока они находятся в одном файле, не имеет значения, являются ли они публичными или частными.
Первый класс, определённый в файле, будет считаться основным, и в него нужно поместить метод main. То есть важна очерёдность.
Собираем программу
Это можно сделать по-разному.
Первый способ
Здесь есть тонкости.
В строке
не должно быть пробелов в конце.
Вторая тонкость описана в [3]: в этой же строке должен стоять перенос на следующую строку. Это если манифест помещается в архив сторонним архиватором.
Программа jar не включит в манифест последнюю строку из манифеста, если в конце не стоит перенос строки.
Ещё момент: в манифесте не должно быть пустых строк между строками. Будет выдана ошибка «java.io.IOException: invalid manifest format».
При использовании команды echo надо следить только за пробелом в конце строки с main-class.
Второй способ
В данном способе избегаем ошибки с пробелом в main-class.
Третий способ
Включили код нужной библиотеки в исполняемый файл.
Как интерпретатор Java выполняет программу HelloUniverse
В JDK 10 модуль запуска Java может работать в трёх режимах:
- Исполнение class-файла.
- Исполнение основного класса из JAR-файла.
- Исполнение основного класса модуля.
- Исполнение класса, объявленного в исходном файле.
Система определяет ваше намерение ввести исходный файл по двум признакам:
- Первый элемент в командной строке не является ни опцией, ни частью опции.
- В строке может присутствовать опция --source .
Во втором случае выбирается режим работы с исходным файлом, и первый элемент в командной строке, который не является опцией, считается исходным файлом, который нужно скомпилировать и запустить.
Если файл не имеет расширения .java, то нужно использовать опцию --source , чтобы принудительно перейти в режим работы с исходным файлом.
Это важно в случаях, когда исходный файл представляет из себя «скрипт», который нужно выполнить, а имя файла не соответствует обычным соглашениям о наименованиях исходных файлов с Java-кодом.
С помощью опции --source можно определять версию языка исходника. Об этом мы поговорим ниже.
Определяем уровень исходного кода с помощью опции --source
Есть два сценария использования опции --source :
- Определение уровня исходного кода.
- Принудительный перевод runtime-среды Java в режим работы с исходным файлом.
Давайте сначала рассмотрим второй сценарий. Переименуем Greater.java просто в greater без расширения и попробуем выполнить:
При отсутствии расширения .java интерпретатор команд ищет скомпилированный класс по имени, переданному в виде аргумента — это первый режим работы модуля запуска Java. Чтобы это не происходило, воспользуемся опцией --source для принудительного переключения в режим работы с исходным файлом:
Теперь перейдём к первому сценарию. Класс Greater.java совместим с JDK 10, поскольку содержит ключевое слово var , но не совместим с JDK 9. Изменим source на 10 :
Снова запустим предыдущую команду, но в этот раз передадим --source 9 вместо 10 :
Обратите внимание: компилятор предупреждает о том, что var стала в JDK 10 ограниченным именем типа. Но поскольку у нас язык уровня 10, компиляция продолжается. Однако возникает сбой, потому что в исходном файле нет типа с именем var .
Всё просто. Теперь рассмотрим использование нескольких классов.
Работа с classpath
Большинство программ зависит от существующего кода. Java предоставляет концепцию classpath. Classpath - это список элементов пути, который будет просматриваться в runtime, что поможет найти зависимый код. При запуске классов Java вы определяете classpath с помощью параметра командной строки -cp :
Java runtime создает classpath , объединяя все классы из всех связанных JAR и добавляя при этом главный класс.
Новые проблемы возникают при дистрибуции JAR, которые зависят от других JAR:
Вам необходимо синхронизировать версии библиотек.
Что еще более важно, аргумент -cp не работает с JAR. Чтобы ссылаться на другие JAR, classpath должен быть задан в манифесте JAR через атрибут Class-Path :
3. По этой причине вам необходимо поместить JAR в то же место, относительное или абсолютное, в целевую файловую систему в соответствии с манифестом. Это означает, что сначала нужно открыть JAR и прочитать манифест.
Одним из способов решения этих проблем является создание уникальной единицы развертывания, которая содержит классы из всех JAR и может быть распространена как один артефакт. Существует несколько вариантов создания таких JAR:
Плагин Spring Boot (Для проектов Spring Boot)
Если удивляет результат
Есть возможность запустить отладчик. Для этого существует jdb.
Сначала компилируем с ключом -g, чтобы у отладчика была информация.
Отладчик запускает свой внутренний терминал для ввода команд. Справку по последним можно вывести с помощью команды help.
Указываем точку прерывания на 9 строке в классе Calculator
Запускаем на выполнение.
Чтобы соориентироваться можно вывести кусок исходного кода, где в данный момент находится курссор.
Узнаем, что из себя представляет переменная а.
Выполним код в текущей строке и увидим, что sum стала равняться 2.
Поднимемся из класса Adder в вызвавший его класс Calculator.
Удаляем точку прерывания
Можно избежать захода в методы, используя команду next.
Проверяем значение выражения и завершаем выполнение.
Хорошо бы протестировать
Запускаем. В качестве разделителя нескольких путей в classpath в Windows используется ';', в Linux — ':'. В консоли Cygwin не работают оба разделителя. Возможно, должен работать ';', но он воспринимается как разделитель команд.
Заключение
В этой статье мы описали 3 различных способа создания самодостаточных исполняемых JAR:
Assembly хорошо подходит для простых проектов
Когда проект становится более сложным и вам нужно работать с дублирующимися файлами, используйте Shade
Наконец, для проектов Spring Boot лучше всего использовать специальный плагин.
Полный исходный код этой статьи можно найти на Github в формате Maven.
Материалы для дополнительного изучения:
Что такое «хороший код» — это во многом спорная тема. Кто-то скажет, что если код работает, значит он достаточно хорош. Кто-то обязательно добавит, что код должен быть легок в понимании и сопровождении. А кто-то добавит, что код еще обязательно должен быть быстрым. Об этом уже много написано и сказано. Что же, давайте еще раз поговорим на эту интересную и холиварную тему. Регистрируйтесь на онлайн-интенсив
Перевод подготовлен в рамках курса "Java Developer. Basic"
Пусть исходный файл HelloUniverse.java содержит определение класса и статичный метод main , который выводит в терминал одну строку текста:
Обычно для запуска этого класса требуется сначала скомпилировать его с помощью Java-компилятора (javac), который создаст файл HelloUniverse.class:
Затем нужно с помощью команды виртуальной машины Java (интерпретатора) запустить получившийся файл:
Тогда сначала запустится виртуалка, которая загрузит класс и исполнит код.
А если вам нужно быстро проверить фрагмент кода? Или вы новичок в Java (в данном случае это ключевой момент) и хотите поэкспериментировать с языком? Описанные два этапа могут всё усложнить.
В Java SE 11 можно напрямую запускать одиночные исходные файлы без промежуточной компиляции.
Эта возможность особенно полезна для новичков, которые хотят поработать с простыми программами. В сочетании с jshell получается прекрасный набор инструментов для обучения начинающих.
Профессионалы могут с помощью этих инструментов изучать нововведения в языке или тестировать незнакомые API. На наш взгляд, лучше автоматизировать многие задачи, вроде написания Java-программ в виде скриптов с последующим исполнением из оболочки ОС. В результате мы можем гибко работать с shell-скриптами и пользоваться всеми возможностями Java. Поговорим об этом подробнее во второй части статьи.
Эта прекрасная возможность Java 11 позволяет напрямую исполнять одиночный исходный файл без компилирования. Давайте обсудим.
Использование библиотеки
Есть программа HelloWorld, которая использует библиотечный класс Calculator. Чтобы скомпилировать и запустить программу, нужно присоединить библиотеку.
Компилируем
Как быть с приложениями JavaEE
Аналогично. Только библиотеки для компиляции нужно брать у сервера приложений, который используется. Если я использую JBoss, то для компиляции сервлета мне нужно будет выполнить примерно следующее
Структура архива JavaEE-приложения должна соответствовать определенному формату. Например
Способы запуска приложения на самом сервере с помощью командной строки для каждого сервера различны.
Надеюсь, данная статья станет для кого-нибудь шпаргалкой для работы с Java в командной строке. Данные навыки помогут понять содержание и смысл Ant-скриптов и ответить на собеседовании на более каверзные вопросы, чем «Какая IDE Вам больше нравится?».
Вопрос такой, если создать .jar или .exe исполняемый файл, он запустится на компьютере на котором нет JVM и не установлены JDK и JRE?
Не знаю насчет .exe (вероятно, зависит от того, как он был создан), но запустить нормально .jar без JRE, который включает в себя JVM, не получится.
Чисто теоретически, exe может таскать все необходимое с собой, дропать, ставить, настраивать и потом запускать дропнутый jar. Вопрос только в том надо ли так извращаться.
3 ответа 3
Вы хорошо подумали?
Есть такие упаковщики, которые в полученный EXE пакуют версию JRE/JVM, в которой и будет запускаться jar.
Например ExcelsiorJET - правда, он коммерческий, но если поискать я думаю есть и бесплатные.
Ну, эм, всё же упаковка целой виртуальной машины в EXE выглядит несколько энтерпрайзно. :) Плюс разворачивать эту самую виртуальную машину при каждом запуске тоже наверное не вполне бесплатно. Но да, это в общем вполне решение.
Действительно интересен размер упакованного небольшого приложения. А точнее, оправдан ли такой кейс для небольших утилит.
Т.к. jar в принципе не может работать без JVM (потому что jar - это набор классов в виде байт-кода, а не в виде исполняемых бинарных файлов), то сформулируем Ваш вопрос так: как в один .exe-файл упаковать свой .jar-проект и JVM. Чтобы Ваш проект мог запускаться и работать без предустановки JVM?
На stackoverflow описали успешный эксперимент по скрещиванию launch4j и JVM (т.е. JRE ).
Перевожу ответ из ссылки:
1.Упаковать свое приложение и JRE в один ZIP-архив со структурой директорий:
2.В xml-файле для launch4j сконфигурировать JRE таким образом:
Фишка состоит в том, что путь указывается не к файлу java.exe. Путь к java.exe указывается относительно позиции .exe-файла.
В данном примере, в качестве JRE, используется обычная копия стандартного установленного JRE-движка (например: C:\Program Files\Java\jre1.8.0_121).
И да, Launch4j - бесплатный и с очень демократичной лицензией BSD 3-Clause License.
Что такое самодостаточный JAR?
JAR — это просто набор файлов классов. Чтобы быть исполняемым, его файл META-INF/MANIFEST.MF должен указывать на класс, реализующий метод main() . Это делается с помощью атрибута Main-Class . Вот пример:
У MainClass метод static main(String… args)
Работает ли этот подход с несколькими классами?
Рассмотрим пример с двумя классами. Код проверяет, является ли заданное строковое значение палиндромом.
Вот код, сохранённый в файле PalindromeChecker.java:
Запустим снова, подставив «RaceCar» вместо «MadAm»:
Теперь подставим «Mohamed» вместо «RaceCar»:
Как видите, можно добавлять в один исходный файл сколько угодно публичных классов. Следите только за тем, чтобы основной метод был определён первым. Интерпретатор будет использовать первый класс в качестве стартовой точки для запуска программы после компилирования кода в памяти.
Запуск исполняемого jar-файла
Файл calculator.jar исполняемым не является. А вот helloworld.jar можно запустить.
Если архив был создан первыми двумя способами, то рядом с ним в одном каталоге должна находится папка lib с файлом calculator.jar. Такие ограничения из-за того, что в манифесте в class-path указан путь относительно исполняемого файла.
При использовании третьего способа нужные библиотеки включаются в исполняемый файл. Держать рядом нужные библиотеки не требуется. Запускается аналогично.
Плагин Apache Assembly
Assembly Plugin для Apache Maven позволяет разработчикам объединять результаты проекта в единый распространяемый архив, который также содержит зависимости, модули, документацию сайта и другие файлы.
— Плагин Apache Maven Assembly
Одним из принципов Maven является создание одного артефакта на проект. Хотя бывают исключения, например, Javadoc и исходный код, но в целом, если вам нужно несколько артефактов, нужно создавать один проект на каждый артефакт. Идея плагина Assembly заключается в том, чтобы обойти это правило.
Плагин Assembly полагается на специальный конфигурационный файл assembly.xml . Он позволяет вам выбирать, какие файлы будут включены в артефакт. Обратите внимание, что конечный артефакт не обязательно должен быть JAR: конфигурационный файл позволяет вам выбирать между доступными форматами, например, zip, war и т.д.
Плагин регулирует общие случаи использования, предоставляя предварительно определенные сборки (assemblies). Среди них - распространение самодостаточных JAR. Конфигурация выглядит следующим образом:
Ссылайтесь на предварительно определенную самодостаточную конфигурацию JAR
Установите главный класс для исполнения
Привяжите к package после формирование исходного JAR
Запуск mvn package дает два артефакта:
Первый JAR имеет то же содержимое, что и тот, который был бы создан без плагина. Второй — это самодостаточный JAR. Вы можете выполнить его следующим образом:
Причина в том, что разные JAR предоставляют разные ресурсы по одному и тому же пути, как например с META-INF/spring.factories .
Зачастую плагин следует стратегии "побеждает последний записавший". Порядок основывается на имени JAR.
С помощью Assembly вы можете нек. Если вам нужно объединить ресурсы, вы, вероятно, захотите использовать плагин Apache Shade.
Надо узнать, что у библиотеки внутри
Можно распаковать архив zip-распаковщиком и посмотреть, какие классы есть в библиотеке.
Информацию о любом классе можно получить с помощью дизассемблера javap.
Из результата видно, что класс содержит кроме пустого конструктора, ещё один метод sum, внутри которого в цикле вызывается метод add класса Adder. По завершении метода sum, вызывается Adder.getSum().
Без ключа -c программа выдаст только список переменных и методов (если использовать -private, то всех).
Читайте также: