java что такое classpath
Создание самодостаточных исполняемых JAR
Когда ваше программное приложение выходит за пределы десятка строк кода, вам, вероятно, следует разделить код на несколько классов. На этом этапе встает вопрос о том, как их распределить. В Java классическим форматом является Java-архив, более известный как JAR. Но реальные программы, вероятно, зависят от других JAR.
Что такое самодостаточный JAR?
У MainClass метод static main(String… args)
Работа с classpath
Новые проблемы возникают при дистрибуции JAR, которые зависят от других JAR:
Вам необходимо синхронизировать версии библиотек.
3. По этой причине вам необходимо поместить JAR в то же место, относительное или абсолютное, в целевую файловую систему в соответствии с манифестом. Это означает, что сначала нужно открыть JAR и прочитать манифест.
Одним из способов решения этих проблем является создание уникальной единицы развертывания, которая содержит классы из всех JAR и может быть распространена как один артефакт. Существует несколько вариантов создания таких JAR:
Плагин Spring Boot (Для проектов Spring Boot)
Плагин Apache Assembly
Assembly Plugin для Apache Maven позволяет разработчикам объединять результаты проекта в единый распространяемый архив, который также содержит зависимости, модули, документацию сайта и другие файлы.
Одним из принципов Maven является создание одного артефакта на проект. Хотя бывают исключения, например, Javadoc и исходный код, но в целом, если вам нужно несколько артефактов, нужно создавать один проект на каждый артефакт. Идея плагина Assembly заключается в том, чтобы обойти это правило.
Ссылайтесь на предварительно определенную самодостаточную конфигурацию JAR
Установите главный класс для исполнения
Привяжите к package после формирование исходного JAR
Запуск mvn package дает два артефакта:
Первый JAR имеет то же содержимое, что и тот, который был бы создан без плагина. Второй — это самодостаточный JAR. Вы можете выполнить его следующим образом:
В зависимости от проекта он может выполняться успешно. или нет. Например, в примере проекта Spring Boot он не работает со следующим сообщением:
Зачастую плагин следует стратегии «побеждает последний записавший». Порядок основывается на имени JAR.
С помощью Assembly вы можете нек. Если вам нужно объединить ресурсы, вы, вероятно, захотите использовать плагин Apache Shade.
Плагин Apache Shade
Плагин Assembly является общим; плагин Shade ориентирован исключительно на задачу создания самодостаточных JAR.
Этот плагин предоставляет возможность упаковать артефакт в uber-jar, включая его зависимости, и оттенить — т.е. переименовать — пакеты некоторых зависимостей.
Плагин основан на концепции преобразователей: каждый преобразователь отвечает за работу с одним типом ресурсов. Преобразователь может копировать ресурс как есть, добавлять статическое содержимое, объединять его с другими и т.д.
Хотя вы можете разработать свой преобразователь, плагин предоставляет набор готовых преобразователей:
Конфигурация плагина Shade к приведенному выше Assembly выглядит следующим образом:
shade привязан к фазе package по умолчанию
Этот преобразователь предназначен для генерации файлов манифеста
Выполните ввод Main-Class
Настройте финальный JAR так, чтобы он был многорелизным JAR. Это необходимо в случае, когда любой из исходных JAR является многорелизным JAR
Запуск mvn package дает два артефакта:
При работе с проектом, взятым за образец, финальный исполняемый файл все еще не работает так, как ожидалось. Действительно, во время сборки появляется множество предупреждений о дублировании ресурсов. Два из них мешают корректной работе проекта. Чтобы правильно их объединить, нам нужно посмотреть на их формат:
META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat: этот Log4J2 файл содержит предварительно скомпилированные данные плагина Log4J2. Он закодирован в двоичном формате, и ни один из готовых преобразователей не может объединить такие файлы. Тем не менее, случайный поиск показывает, что кто-то уже занимался этой проблемой и выпустил преобразователь для работы с объединением.
META-INF/spring.factories : эти файлы, специфичные для Spring, они имеют формат «один ключ/много значений». Поскольку они текстовые, ни один готовый преобразователь не может корректно объединить их. Однако разработчики Spring предоставляют такую возможность (и многое другое) в своем плагине.
Чтобы настроить эти преобразователи, нам нужно добавить вышеуказанные библиотеки в качестве зависимостей к плагину Shade:
Объедините файлы /META-INF/spring.factories
Добавьте необходимый код для преобразователей
Эта конфигурация работает! Тем не менее, есть оставшиеся предупреждения:
Лицензии, предупреждения и схожие файлы
Файлы конфигурации Service Loader
Вы можете добавить и настроить дополнительные преобразователи для устранения вышеупомянутых пунктов. В целом, весь процесс требует глубокого понимания каждого вида ресурсов и знаний от том, как с ними работать.
Плагин Spring Boot
Плагин Spring Boot использует совершенно другой подход. Он не объединяет ресурсы из JAR по отдельности; он добавляет зависимые JAR по мере их появления в uber JAR. Для загрузки классов и ресурсов он предоставляет специальный механизм. Очевидно, что он предназначен для проектов Spring Boot.
Настройка плагина Spring Boot проста:
Давайте проверим структуру финального JAR:
Скомпилированные классы проекта
Загрузка классов в Spring Boot
Вот выдержка из манифеста по образцу проекта:
Как вы можете видеть, главный класс является специфичным классом Spring Boot, в то время как «настоящий» главный класс упоминается в другой записи.
Для получения дополнительной информации о структуре JAR, пожалуйста, ознакомьтесь со справочной документацией.
Заключение
В этой статье мы описали 3 различных способа создания самодостаточных исполняемых JAR:
Assembly хорошо подходит для простых проектов
Когда проект становится более сложным и вам нужно работать с дублирующимися файлами, используйте Shade
Наконец, для проектов Spring Boot лучше всего использовать специальный плагин.
Полный исходный код этой статьи можно найти на Github в формате Maven.
Материалы для дополнительного изучения:
Что такое «хороший код» — это во многом спорная тема. Кто-то скажет, что если код работает, значит он достаточно хорош. Кто-то обязательно добавит, что код должен быть легок в понимании и сопровождении. А кто-то добавит, что код еще обязательно должен быть быстрым. Об этом уже много написано и сказано. Что же, давайте еще раз поговорим на эту интересную и холиварную тему. Регистрируйтесь на онлайн-интенсив
Pro Java
Страницы
3 апр. 2015 г.
Разбираемся с classpath в Java. Часть 1.
Теперь быстренько разберемся classpath, так как это достаточно важная тема для разработки на Java. Естественно я тут не смогу разобрать все тонкости и хитрости, но постараюсь представить самое необходимое чтобы было понимание этой темы. Для желающих разобраться глубже приведу линки для самостоятельного изучения. Ну и как всегда гугль в помощь.
Чтобы понимать что происходит под капотом у любой Java IDE когда она собирает проект и запускает его надо хоть немного попрактиковаться в использовании компилятора javac, среды исполнения java и понять classpath.
По существу classpath указывает компилятору или виртуальной машине где искать классы необходимые для сборки проекта или же его запуска. Немного об этом мы уже узнали тут. Теперь разберемся с этим получше.
Указать где компилятору или виртуальной машине искать классы можно через ключ –classpath или же системную переменную окружения CLASSPATH. Мы рассмотрим оба этих варианта.
Начнем с простого. Вернемся к нашему проекту Hello World (00004E_HelloWorld), там где мы разделили его на два файла Hello.java и Word.java.
Теперь попробуем создать исполняемый (jar) файл этого проекта из среды Eclipse. Так как скомпилированные, читай готовые к исполнению, файлы в Java имеют расширение class, а классов в реальных программах, могут быть сотни и тысячи, то их собирают в один или несколько jar архивов и таким образом запускают. То есть уже существует не россыпь файлов с расширением class, а один или несколько jar файлов.
И так! Понеслась! Воспользуемся Export для создания jar
После этого мы получим файл HelloWorld.jar готовый к исполнению на виртуальной машине java. Запустим его из командной строки:
Запускать jar файлы надо с ключом –jar как показано на скрине выше. Если этот ключ не использовать то вылетит ошибка:
Почему строчка запуска выглядит именно так? Вспоминаем что именно класс Hello.java содержит у нас метод main.
Класс Word.java такого метода не имеет.
Как я уже говорил метод main – это точка входа в программу, то есть место от куда начинается ее выполнение и поэтому виртуальной машине java надо знать, от куда надо начинать выполнять программу. Если она не может найти метод main, то она начинает ругаться, как это было показано выше.
И так в нашей строчке
Для начал просто скомпилируем исходники в class файлы без упаковки их в jar, чтобы было понятнее.
Переходим в коневой каталог 00004E_HelloWorld и от туда даем команду компиляции
Поскольку у нас программа состоит из двух классов Hello и Word, то их обоих сразу надо указать компилятору. Кроме того так же надо указать и кодировочку исходников. Так же мы указали папку bin – это то куда будут складываться откомпилированные файлы.
Теперь у нас в каталоге bin два файла Hello.class и Word.class. Перейдем в него чтобы запустить программу.
jar cf HelloWorld.jar Hello.class Word.class
и попробуем запустить HelloWorld.jar
И вылетела ошибочка. Почему так? Ведь у нас уже есть jar файл в который упакованы оба класса.
Но все равно не работает. Это происходит потому, что внутри jar файла мы не указали какой файл у нас имеет метод main.
Запустить наш jar файл все таки можно указав дополнительно, какой класс содержит метод main.
Теперь все работает. Но согласитесь так запускать jar файл не удобно, так как всегда надо знать какой класс содержит метод main. Если вы смотрели внимательно, то видимо заметили внутри архива HelloWorld.jar папку META-INF. В ней содержится файл MANIFEST.MF
Вот в нем и должна содержаться информация о классе содержащем метод main, но пока в нем ее нет.
Исправим эту ошибочку. Удалим файлик HelloWorld.jar и создадим его заново, но уже с добавлением информации о классе содержащим метод main. Сделаем это следующей командой
jar cfe HelloWorld.jar Hello Hello.class Word.class
И запустим файл HelloWorld.jar уже как полагается без танцев с бубном.
Как видим все работает нормально. Это произошло потому, что файл MANIFEST.MF уже содержит информацию о классе содержащем метод main.
Ну вот теперь мы имеем хоть какое-то представление о том что происходит когда какая-либо IDE создает исполняемый jar файл, а так же получили представление о classpath. В следующей статье мы немного углубим его.
P.S. Так же стоит знать что по умолчанию для виртуальной машины java доступны все классы стандартной библиотеки java, а так же все классы в текущем каталоге от куда запускается главный класс содержащий метод main.
Ну и на последок ссылка где про classpath рассказано достаточно подробно. Правда я не знаю как долго она проживет.
Управление Java classpath (Windows) (исходники)
Качественная IDE, например Eclipse, может оградить Вас от некоторых трудностей управления classpath, но только частично, и только до тех пор, пока все гладко (а что-то всегда оказывается наперекосяк). Следовательно, совершенно необходимо, чтобы каждый программист на Java понимал classpath в полном объеме. Только при глубоком понимании можно надеяться на разрешение проблем, возникающих из-за classpath.
В данной статье описано все, что Вам необходимо знать о Java classpath (и сопутствующем sourcepath) в Windows. В соответствующей статье продемонстрированы аналогичные методы для UNIX и Mac OS X. Соблюдение указанного алгоритма должно не только предотвратить возникновение лишних трудностей с classpath, но и устранить большинство возникающих проблем.
Имя пакета должно состоять исключительно из символов ASCII. Хотя компилятор и принимает написание имен пакетов на иврите, кириллице, греческом и других шрифтах, но многие файловые системы этого не допускают и, как Вы вскоре увидите, эти имена пакетов будут иметь двойное назначение в качестве имен директорий. Хотя имена классов и пакетов Java и пишутся в Unicode, многие файловые системы, включая FAT, пока не работают с Unicode. Печально, но в этом списке все еще присутствует много файловых систем FAT. Простое копирование файла в систему с другой кодировкой, заданной по умолчанию, может помешать компилятору и интерпретатору при поиске правильных классов.
Не экономьте на имени пакета, а то это приведет к катастрофе! Если Вам необходимо имя домена, то купите его. Если имена слишком длинные, то купите покороче. (Однажды я купил xom.nu, следовательно, префикс моего пакета состоял всего лишь из шести букв.) Не помещайте свои классы в пакете, заданном по умолчанию (в том пакете, который Вы получаете, если не включили выражение пакета в класс). Если доступ к пакету не позволяет объектам взаимодействовать, добавьте в классы больше общих методов. Каждый класс, который Вы используете более одного раза, должен находиться в пакете.
Расширения файлов и пути доступа очень важны для Java и Windows. Однако прежде чем перейти к следующему этапу, убедитесь, что Вы можете их посмотреть. Скрытие части имени файла может быть приемлемым для конечного пользователя (хотя у меня есть сомнения на этот счет), но это совершенно не годится для разработчика. Чтобы это исправить, необходимо изменить некоторые заданные по умолчанию настройки в Windows Explorer.
Сначала откройте папку на рабочем столе Windows, не важно какую. Зайдите в меню Сервис(Tools) и выберите Свойства папки (Folder Options). В открывшемся диалоговом окне убедитесь, что отмечены три следующих опции, как показано на рисунке 1:
Рисунок 1. Свойства Windows Explorer
Возможно, появится желание отметить и пункт «Показывать скрытые файлы и папки (Show hidden files and folders).» Это не значительно повлияет на Вашу работу с Java, но лично я предпочитаю видеть, с чем я работаю. Настройка данных свойств предоставляет более подробную информацию о том, что и где Вы делаете, и помогает гораздо легче устранять любые потенциальные проблемы.
Что бы Вы не делали, не сохраняйте данную директорию (или любую другую) в Вашей папке JDK (например, C:\jdk1.6.0 или C:\Program Files\Java\j2sdk1.5.0_09). После первой инсталляции директории JDK и JRE должны остаться нетронутыми.
Внутри директории project создайте еще две директории: bin и src. (Некоторые называют их build и source, соответственно.)
Рисунок 2. Структура директорий, повторяющая структуру пакета
Программисты UNIX, которые изобрели язык Java, позволили компилятору признавать прямые слэши, характерные для UNIX, вместо используемых в Windows обратных слэшей. Следовательно, следующая команда будет также работать:
Однако некоторые команды не допускают использования прямых слэшей и требуют обратные. При работе в Windows проще всего всегда использовать обратные слэши.
По умолчанию компилятор javac считает, что все они являются текущей рабочей директорией, что почти никогда не соответствует Вашим нуждам. Следовательно, при компиляции Вам необходимо точно указать каждый из этих элементов.
Если путь является неверным, то Вы получите сообщение об ошибке такого типа:
Если у Вас появилось сообщение об ошибке, то проверьте правильность указания путь. В данном случае, ошибка говорит о том, что переставлены местами вторая и третья буквы в «math».
Данное сообщение обычно указывает на опечатку в указании пути, но она также может означать, что Вы находитесь не в той директории, в которой предполагаете. В данном примере Вам необходимо удостовериться, что текущей рабочей директорией является именно директория project. Проверьте текст между C: и > в командной строке, чтобы удостовериться, что Вы находитесь там, где должны быть. В данном примере Вы увидите, что в действительности я нахожусь в C:\project\src, хотя должен быть в C:\project.
Теперь результат выглядит так, как показано на рисунке 3. Обратите внимание, что javac создал полную иерархию директорий com\elharo\math. Вам не надо делать этого вручную.
Рисунок 3. Параллельные исходные и скомпилированные иерархии
Рисунок 4. Исходная структура для нескольких пакетов
Теперь давайте посмотрим, что случится, если я попытаюсь скомпилировать MainFrame.java так, как я делал это ранее.
Теперь программа компилируется без ошибок и выдает результат, показанный на рисунке 5. Обратите внимание, что javac также скомпилировал файл Fraction.java, на который ссылается скомпилированный мной файл.
Рисунок 5. Мультиклассовый результат
В Вашем sourcepath может быть несколько директорий, отделенных точкой с запятой, хотя обычно в этом нет необходимости. Например, если Вы хотите включить как локальную директорию src, так и директорию C:\Projects\XOM\src, где хранится исходный код для другого проекта, то можно скомпилировать следующее:
Ни имена классов Java, ни имена пакетов не могут содержать пробелов. Однако иногда директория, содержащая исходный файл или директорию пакета Java все-таки содержит пробел. Самым наглядным примером служит Documents and Settings. Если Вам необходимо включить одну из директорий в путь, то необходимо указать соответствующий аргумент командной строки в двойных кавычках. Например, если Вы находитесь в корневой директории C:, а папка src находится в C:\Documents and Settings\Administrator\project, то необходимо выполнять компиляцию следующим образом:
В большинстве случаев этого можно избежать, перейдя в директорию project, до компиляции или запуска программы.
В больших проектах повторная компиляция каждого файла может быть достаточно затратной с точки зрения времени. Вы можете облегчить нагрузку путем раздельного компилирования и хранения отдельных частей одного и того же проекта в различных директориях bin. Данные директории добавляются в classpath.
Теперь предположим, что мне необходимо добавить две директории, C:\project1\classes и C:\project2\classes. Тогда укажем их, разделив точкой с запятой следующим образом:
Конечно, при желании Вы можете использовать различные варианты относительного пути. Например, если project1 и project2 являются одноуровневыми узлами текущей рабочей директории (то есть, у них одна и та же родительская директория), то можно указать их следующим образом:
|
Итак, теперь Вы успешно скомпилировали программу и готовы ее запустить. Это похоже на компиляцию, но гораздо проще. При запуске программы необходимо указать только:
Не нужно указывать sourcepath.
Обычно classpath является тем же самым classpath, который Вы использовали для компиляции программы, с добавлением директории, где был размещен скомпилированный результат. Например, если команда компиляции была следующей:
В данном разделе я покажу Вам несколько мест, где можно найти скрытые файлы, которые внезапно появляются в Вашем classpath и создают проблемы. Чаще всего это происходит на тех компьютерах, которые Вы не контролируете, например на сервере.
Не поддавайтесь искушению, так как это может привести к возникновению проблем при загрузке неверного класса или неверной версии класса. В итоге Вы потратите в сотни раз больше времени на устранение неполадок, вызванных случайной загрузкой неверных классов, чем сэкономите на наборе. Существуют и лучшие способы автоматизации classpaths и избавления от ввода с клавиатуры.
Эта проблема может оказаться особенно серьезной при развертывании серверных приложений. Будьте особенно внимательны к тому, чтобы на том сервере, на котором Вы разворачиваете, не было каких-либо дополнительных JAR в директории jre\lib\ext. Проблемы, вызванные неверной версией JAR-архива в classpath, могут оказаться очень сложными с точки зрения устранения, если Вы не распознаете симптомы и не будете знать, что искать. Во избежание таких проблем, некоторые среды разработки продвинулись так далеко, как написание своих собственных загрузчиков класса, которые обходят обычные загрузочные механизмы класса кода Java.
Вы должны научиться пользоваться обычным молотком, прежде чем хвататься за пневматический «пистолет». Аналогично, Вы должны уверенно управлять классами вручную, прежде чем начинать пользоваться более мощными инструментами. Хотя, как только Вы освоите набор инструментов командной строки, Вы захотите использовать инструмент, автоматизирующий часть рутинной работы с sourcepath и classpath. В основном данные инструменты выполняют работу по организации файлов согласно линиям, которые я уже проложил в данной статье.
Рисунок 6. Быстрая настройка для classpath в Eclipse
Имейте в виду, что данные IDE все еще находятся на вершине файловой системы, которая должна быть задана надлежащим способом, особенно если Вам необходимо интегрировать в другие инструменты и другие IDE. Основное преимущество данных инструментов заключается в том, что древовидные списки, таблицы и диалоговые окна GUI заменяют переключатели командной строки, но основная файловая структура остается такой же.
Maven пошел еще дальше, чем Ant в организации и автоматизации процесса конструирования classpath и связанных с ним задач. Maven по умолчанию предлагает рациональную настройку, которая позволяет конструировать простые проекты с помощью всего лишь нескольких строк кода, если только Вы разместили исходные файлы там, где Maven будет их искать. Вам все еще придется координировать иерархию файловой системы и иерархию пакета. Maven особенно хорош для управления взаимозависимостями посторонних библиотек, хотя он и не так легко приспосабливается, как Ant.
Хотя использование classpath и является очень хлопотным, но Вы можете приручить его с помощью нескольких нехитрых правил. А именно:
И самая последняя рекомендация: большинство отнимающих много времени проблем с classpath вращаются вокруг таких простых ошибок, как ошибочное написание имени директории или компилирование из неверной директории. Если Вы не можете сразу определить, в чем проблема, обратитесь к друзьям или коллегам, чтобы они на нее посмотрели. Зачастую я нахожусь слишком близко к проблеме, чтобы разглядеть неполадку в моей настройке, которая совершенно очевидна кому-то еще. Вторая пара глаз оказывается очень эффективным методом отладки.
Конечно, classpath совсем не прост, но есть управа и на его сумасбродство, которая делает его послушным. Немного аккуратности и внимания, потраченных на соглашение по присвоению имен, аргументы командной строки и структуры директорий позволят Вам скомпилировать и запустить программы при минимуме суеты.