Руководство по Java 9 для тех, кому приходится работать с legacy-кодом
Руководство по Java 9: компиляция и запуск проекта
Руководство по Java 9: компиляция и запуск проекта
- Переводы , 18 июля 2017 в 20:40
Команды java и javac редко используются Java-программистами. Такие инструменты, как Maven и Gradle делают их почти не нужными. Однако Maven и Gradle до сих пор не предоставляют полную поддержку для Java 9, поэтому, если вы хотите начать использовать её уже сейчас или просто хотите узнать некоторые полезные тонкости до официального релиза, стоит научиться вызывать java , javac и jar для управления своим кодом.
Статья призвана показать примеры использования этих команд, а также то, как эти команды изменились по сравнению с прошлыми версиями Java. Дополнительно будут рассмотрены новые инструменты: jdeps и jlink . Предполагается, что вы хоть немного знакомы с предыдущими версиями команд java / javac / jar и с модульной системой Java 9.
Установка Java 9
Сперва необходимо установить Java 9. Вы можете скачать её с сайта Oracle, но рекомендуется использовать SdkMAN!, так как в будущем он позволит вам с легкостью переключаться между разными версиями Java.
Можно установить SdkMAN! с помощью этой команды:
Посмотрите, какая сборка является последней:
Затем установите Java 9:
Теперь, если у вас установлены другие версии Java, вы можете переключаться между ними с помощью команды:
Компиляция и запуск «по-старому»
Для начала напишем какой-нибудь код, чтобы проверить наши инструменты. Если не использовать модульный дескриптор, то все выглядит так же, как и раньше.
Возьмем этот простой Java-класс:
Теперь, так как мы не использовали никаких особенностей Java 9, мы можем скомпилировать всё как обычно:
Команда создаст файл класса out/app/Main.class . Запустить его можно так же, как и в прошлых версиях:
Программа выведет Hello Java 9 .
Ивент перенесён на 5 июня в 08:30 в 08:30, Киев, от 170 до 200 $
Теперь создадим библиотеку Greeting также без особенностей Java 9, чтобы посмотреть, как это работает.
Создадим файл greeting/ser/lib/Greeting.java со следующим кодом:
Изменим класс Main для использования нашей библиотеки:
Скомпилируем эту библиотеку:
Чтобы показать, как работают оригинальные Java-библиотеки, мы превратим эту библиотеку в jar-файл без дескрипторов модулей Java 9:
Команда создаст файл libs/lib.jar , содержащий класс lib.Greeting .
Просмотреть информацию о jar-файле можно с помощью опции tf :
Команда должна вывести:
Теперь для компиляция app.Main нам необходимо указать компилятору, где найти класс lib.Greeting .
Используем для этого cp (classpath):
И то же самое для запуска программы:
Мы можем упаковать приложение в jar-файл:
И затем запустить его:
Вот так выглядит структура нашего проекта на данный момент:
Модуляризация проекта
Пока что ничего нового, но давайте начнем модуляризацию нашего проекта. Для этого создадим модульный дескриптор (всегда называется module-info.java и размещается в корневой директории src/ ):
Команда для компиляции модуля в Java 9 отличается от того, что мы видели раньше. Использование старой команды с добавлением модуля к списку файлов приводит к ошибке:
Чтобы понять, почему наш код не компилируется, необходимо понять, что такое безымянные модули.
Любой класс, который загружается не из именованного модуля, автоматически выполняет часть безымянного модуля. В примере выше перед созданием модульного дескриптора наш код не был частью какого-либо модуля, следовательно, он был ассоциирован с безымянным модулем. Безымянный модуль — это механизм совместимости. Проще говоря, это позволяет разработчику использовать в приложениях Java 9 код, который не был модуляризирован. По этой причине код, относящийся к безымянному модулю, имеет правила сродни Java 8 и ранее: он может видеть все пакеты, экспортируемые из других модулей, и все пакеты безымянного модуля.
Когда модульный дескриптор добавляется к модулю, его код больше не является частью безымянного модуля и не может видеть код других модулей, пока не импортирует их. В случае выше модуль com.app не требует никаких модулей, поэтому модуль библиотеки Greeting для него не виден. Он может видеть только пакеты модуля java.base .
Модули в Java 9, за исключением неуловимого безымянного модуля описанного выше, должны объявлять, какие другие модули им необходимы. В случае с модулем com.app единственным требованием является библиотека Greeting. Но, как вы могли догадаться, эта библиотека (как и другие библиотеки, не поддерживающие Java 9) не является модулем Java 9. Как же нам включить её в проект?
В таком случае вам нужно знать имя jar-файла. Если у вас есть зависимость от библиотеки, которая не была конвертирована в модуль Java 9, вам надо знать, какой jar-файл вызывается для этой библиотеки, потому что Java 9 переведёт имя файла в валидный модуль.
Это называется автоматический модуль.
Так же, как и безымянные модули, автоматические модули могут читать из других модулей, и все их пакеты являются экспортируемыми. Но, в отличие от безымянных модулей, на автоматические можно ссылаться из явных модулей.
Чтобы узнать имя автоматического модуля, компилятор конвертирует неальфанумерические, поэтому что-то вроде slf4j-api-1.7.25.jar превратится в имя модуля sl4j.api .
У нас есть библиотека с именем lib.jar . Давайте переименуем jar-файл в greetings-1.0.jar :
Это более стандартное имя файла, и теперь мы можем сказать Java включить автоматический модуль с приемлемым именем greetings . И можем вызывать его из com.app модуля:
Модули не добавлены в classpath . Как и обычные jar-файлы, они используют новый флаг –module-path (-p) . Теперь мы можем скомпилировать наши модули следующей командой:
Чтобы запустить app.Main командой java мы можем использовать новый флаг –module (-m) , который принимает либо имя модуля, либо шаблон module-name/main-class :
И мы получим вывод Hi, there .
Для создания и использования app.jar в качестве исполняемого jar-файла выполните следующие команды:
Следующим шагом будет модуляризация библиотек, которые используются нашим приложением.
Модуляризация библиотек
Для модуляризации библиотеки нельзя сделать ничего лучше, чем использовать jdeps — инструмент для статистического анализа, который является частью Java SE.
Например, команда, которая позволяет увидеть зависимости нашей небольшой библиотеки, выглядит так:
А вот результат её выполнения:
Как и ожидалось, библиотека зависит только от java.base модуля.
Мы знаем, что com.app зависит от модуля greetings . Давайте попробуем использовать jdeps , чтобы он подтвердил нам это. Для этого нужно удалить файлы module-info.calss и app.jar и затем запустить jdeps :
Хорошо, но можно лучше. Мы можем попросить jdeps автоматически сгенерировать модульный дескриптор для набора jar-файлов. Просто укажите ему, куда сохранять сгенерированные файлы (например, в папку generated-mods ), и где находятся jar-файлы:
Команда создаст два файла: generated-mods/app/module-info.java и generated-mods/greetings/module-info.java со следующим содержимым:
Теперь мы можем добавить сгенерированный дескриптор для нашей библиотеки в её исходный код, переупаковать её, и у нас получится полностью модульное приложение:
Теперь у нас есть полностью модуляризированные библиотека и приложение. После удаления сгенерированных и бинарных файлов, структура нашего приложения выглядит следующим образом:
Обратите внимание, что для получения хороших данных от jdeps вы должны предоставить местоположение всех jar-файлов, которые используются в приложении, чтобы он мог составить полный граф модуля.
Наиболее простым способом получить список всех jar-файлов, которые используются в библиотеке, является использование скрипта Gradle. Он выведет пути локальных jar-файлов для всех зависимостей библиотек, которые вы добавите в секцию зависимостей, и скачает их, если необходимо:
Если у вас нет Gradle, вы можете использовать SdkMAN! для его установки:
Для получения списка зависимостей используйте следующую команду:
Полученную информацию передайте jdeps для анализа и автоматической генерации метаданных.
Это файл, который jdeps выводит для javaslang.match :
Создание собственного образа среды выполнения
С помощью jlink Java-приложения могут распространяться как образы, которые не требуют установки JVM.
Следующая команда создает образ для нашего com.app модуля без оптимизации, сжатия или отладочной информации:
Меньший размер может быть достигнут использованием некоторых флагов jlink , таких как –strip-debug и –compress :
Размер пакетов можно посмотреть с помощью команды du -sh :
Для запуска приложения используйте предоставляемый лаунчер в директории bin :
Вы должны увидеть сообщение Hi there .
На этом всё. Разбор нововведений в Java 9 предлагаем прочитать в нашей статье.
Руководство по Java 9 для тех, кому приходится работать с legacy-кодом
Наверняка вы слышали о Java Platform Module System (JPMS), которая появилась в Java 9. Т.к. она вызывает множество проблем совместимости, с которыми вы столкнетесь во время перехода с Java 8, то это определенно будет подталкивать вас к тому, чтобы понять основы JPMS. Однако имейте в виду, вам не обязательно создавать модули для запуска вашего кода на Java 9 или на более новых версиях!
Вам не нужны модули для работы на Java 9+
В Java SE было много кода, который фактически был связан с Java EE. Он присутствовал в шести модулях, которые были объявлены кандидатами на удаление в Java 9 и удалены в Java 11. Вот удаленные технологии и пакеты:
- JavaBeans Activation Framework (JAF) в javax.activation
- CORBA в пакетах javax.activity, javax.rmi, javax.rmi.CORBA и org.omg.*
- Java Transaction API (JTA) из пакета javax.transaction
- JAXB в пакетах javax.xml.bind.*
- JAX-WS в пакетах javax.jws, javax.jws.soap, javax.xml.soap, и javax.xml.ws.*
- Commons Annotation в пакете javax.annotation
Добавьте сторонние зависимости, которые содержат нужные вам классы. Самый простой способ сделать это — использовать эталонные реализации зависимостей (заданные с помощью Maven, без указания версий — используйте самую последнюю):
Одним из наибольших преимуществ модульных систем является строгая инкапсуляция. Она гарантирует, что непубличные классы, а также классы из внутренних пакетов недоступны извне модуля. Прежде всего, это, конечно, относится к модулям платформы, поставляемым с JDK, где полностью поддерживаются только пакеты java. * и javax. *. С другой стороны, большинство пакетов com.sun. * и sun.* являются внутренними и следовательно недоступны по умолчанию
Хотя компилятор Java 11 на этапе компиляции ведет себя так, как вы ожидаете, и предотвращает незаконный доступ, то же самое не относится к этапу выполнения. Это предполагает гибкую обратную совместимость, облегчает переход и повышает шансы приложений, разработанных на Java 8, выполняться на Java 11. Если для доступа используется механизм reflection, то выдается предупреждение
Наиболее очевидным решением проблемы зависимостей от внутренних API-интерфейсов является избавление от этих зависимостей. Замените их внешними API-интерфейсами, и вы уменьшите технические риски
Если это не может быть сделано по какой-либо причине, следующая лучшая вещь — признать существование таких зависимостей и сообщить системе, что вам нужно получить к ним доступ. Для этого вы можете использовать две опции командной строки:
- Параметр –add-exports module / package = $readmodule экспортирует $package из $module в $readmodule. Таким образом, код из $readmodule получает доступ ко всем открытым типам в $package, но другие модули это сделать не смогут. При установке флага $readmodule ALL-UNNAMED, весь код, расположенный в classpath, может получить доступ к этому пакету. Во время перехода на Java 11 вы всегда будете использовать эту опцию (вам придется изменить ее при модуляции). Она доступна для команд java и javac
- Способ, описанный выше, предоставляет доступ к открытым членам public типов, но reflection может сделать больше: при использовании setAccessible(true) механизм reflection позволяет взаимодействовать с приватными классами, полями, конструкторами и методами (иногда это называется deep reflection). Параметр java –add-opens использует тот же синтаксис, что и -add-exports, но открывает пакет для deep reflection. Это означает, что все его типы и их члены доступны независимо от их модификаторов видимости
Очевидно, вам нужен -add-exports, на этапе компиляции, но совместное использование -add-exports и -add-opens на этапе выполнения будет иметь свои преимущества:
- Механизм получения прав доступа во время выполнения кода может измениться в будущих релизах Java, и потому вам надо это сделать в любом случае
- –add-opens делает предупреждения об использовании механизма рефлексии
- Через минуту вы сможете убедиться в том, что при этом никаких новых зависимостей не возникнет, и во время исполнения будет применяться строгая инкапсуляция
Проблемы при переходе на Java 9
В конце мая я выступал на JEEConf 2017, где рассказывал об эффективности и производительности Java библиотек и фреймворков. Любое упоминание о производительности должно ссылаться на результаты измерений, что я и сделал во время доклада. Но я решил пойти дальше и протестировать один и тот же код на двух версиях — Java 8 и Java 9.
Хоть Java 9 и выходит только в июле (уже в сентябре), но она уже перешла в стадию Release Candidate, то есть никаких новых фич больше не будет и все критические баги должны быть исправлены. Я начал пользоваться Java 8 за 5 месяцев до релиза и никаких особых проблем при этом не испытал. В то же время главная фича новой версии — модуляризация (проект Jigsaw) привела к многочисленным изменениям внутри самой JDK. Хорошо было бы протестировать библиотеки на новой версии. В сети есть много статей и видео-докладов на тему проектирования модулей. Но сначала нужно проверить, а будет ли работать существующее приложение, и если будет, то как отразится миграция на скорости работы нашей системы. Не секрет, что многие компании остаются на старых версиях (Java 5 — 7) частично из-за боязни новых ошибок и проблем в производительности. Если же приложение работать не будет, насколько просто это починить?
Поэтому я призываю всех установить JDK 9 и протестировать свое приложение. Разумеется, вы можете и дальше использовать текущую версию Java, но прогресс не стоит на месте. Через пару лет выйдет Java 10, а с ней возможно и свойства, и value types, и все равно придется использовать модульность. Тем более что современные IDE (IntelliJ IDEA) уже поддерживают все фичи Java 9.
Системы сборки. Maven
Сборка проекта — это процесс, который проходит постоянно в течение рабочего дня как на компьютере разработчика, так и на CI сервере. Поэтому здесь требуется максимальное быстродействие. Для тестирования сборки я выбрал проект, который я разрабатываю в рамках книги «Разработка Java приложений», тем более что он поддерживает и Maven, и Gradle. Проект постоянно обновляется, поэтому версии зависимостей в нем 2017-го года либо конца 2016-го года. Вы сами можете скачать проект и протестировать на своей машине.
Поддерживают ли Maven/Gradle Java 9? Это интересный вопрос, тем более что их модель зависимостей чем-то похожа и даже является конкурентом JDK 9.
Итак, я установил JDK Early Access preview build 171 на Windows систему. Результат запуска команды «java -version»:
Начнем с Maven. Для тестирования я выбрал последнюю версию — 3.5.0. Сам Maven запускается без проблем, выдает корректную версию JDK:
Теперь запустим сборку проекта: mvn clean install. Сборка падает на этапе запуска тестов с ошибкой:
Почему-то стандартный класс JAXBException больше не находится. Дело в том, что все, связанное с JAXB, перенесено 9-й Java в модуль java.xml.bind, который автоматически не добавляется в modulepath при запуске JVM, потому что не входит в дефолтный модуль java . se. Как выйти из этого положения? Либо явно указать этот модуль для запуска JVM, либо агрегатный модуль java . se . ee, куда входит все, что связано с Java EE API. Разумеется, добавить агрегатный модуль удобнее, чем каждый модуль по отдельности. Поэтому добавляем переменную окружения MAVEN_OPTS (если у вас ее еще нет), и присваиваем ей значение «—add-modules java . se . ee».
Однако ошибка не исчезает. Все дело в том, что тесты запускает Maven Surfire плагин, который стартует новый JVM процесс. Для того, чтобы указать новые модули, нужно добавить Surfire плагин в главный pom.xml и передать параметры там:
Перезапускаем сборку и получаем новое исключение:
Эта проблема не имеет очевидного решения, потому что JTA библиотека (где находится класс SystemException) присутствует в class-path. Поэтому придется все же в Surfire плагине использовать модуль java.xml.bind:
Перезапускаем сборку Maven и получаем новое исключение:
В чем заключается ошибка? Hibernate пытается через reflection сделать публичным метод, который является protected или private. До Java 9 это было небезопасно, но возможно. Сейчас это запрещено, если только модуль, в котором находится метод, не объявлен открытым (open). Однако есть лазейка, помогает обойти это ограничение. Нужно добавить к аргументам JVM строку —add-opens java . base/java . lang=ALL-UNNAMED
Здесь мы указываем название модуля, затем пакет и тот модуль, для которого мы открываем доступ. Так как наш проект не использует модули, то мы указываем константу ALL-UNNAMED, которой открыт доступ для всех анонимных модулей. Анонимные модули — это обычные jar файлы, в которых нет файла конфигурации module-info . class. Они были добавлены, чтобы разработчикам библиотек было легче перейти на Java 9.
Добавляем эту строку к аргументам командной строки для плагина Surfire и перезапускаем сборку Maven. Получаем новое исключение, но уже при запуске других тестов:
Как вы видите из stacktrace, проблема происходит на стадии инициализации JMockit. Что можно сделать? Можно обновить версию Jmockit с 1.30 на 1.32. Но это не помогает. Уже готовится 2-я версия JMockit, но она все еще в стадии beta. Анализ исходников JDK позволяет обнаружить системную переменную JVM, которая позволяет присоединиться к текущей JVM из нее самое. Нужно лишь установить этот флаг в true: -Djdk.attach.allowAttachSelf=true. Подробнее можно прочесть здесь
Добавляем флаг к параметрам в Surefire плагине и перезапускаем сборку Maven. Получаем новую ошибку:
Подробнее об этой проблеме можно прочесть здесь.
А лечится она обновлением версии war-плагина с 2.6 на 3.1.0:
Меняем версию и перезапускаем сборку. Получаем новую ошибку:
С похожей проблемой мы уже сталкивались. Нужно лишь «открыть» доступ к пакетам, добавив параметры в MAVEN_OPTS:
Перезапускаем сборку Maven и получаем новую ошибку:
Stacktrace говорит об ошибке более подробно:
Проблема в том, что такого класса больше нет в JDK. Можно вручную добавить необходимые классы, как сделали здесь.
А можно обновить версию Lombok (c 1.16.14 до 1.16.16):
Перезапускаем сборку Maven и получаем новое исключение при запуске тестов:
После небольшого исследования выяснилось, что проблема исходит от библиотеки тестирования JGlue, однако его последняя версия была выпущена в июле 2016 года и неизвестно, насколько быстро выйдет новая версия.
На этом мое тестирование Maven завершилось. Часть проблем удалось решить, но в целом, их довольно много как для одного проекта. Проект собирается успешно, но часть тестов при этом не проходит.
Системы сборки. Gradle
Теперь проверим, насколько совместим Gradle с JDK 9. Возьмем последнюю версию первого — 3.5.0, обновим Lombok зависимость и запустим сборку: gradle clean install.
Получим довольно малоинформативную ошибку: java.lang.ExceptionInInitializerError (no error message). Поэтому добавим флаг —stacktrace, который даст больше информации:
Для того, чтобы избавиться от этой ошибки, добавляем знакомый аргумент add-opens в системную переменную GRADLE_OPTS:
Подробнее можно прочитать здесь и здесь.
Перезапускаем сборку Gradle, получаем новую ошибку:
В результат длительного исследования оказывается, что ситуация не так проста, как для Maven. Дело в том, что во время сборки в Gradle запускается несколько JVM процессов, одни процессы могут порождать другие. Можно сделать fork процессов и главное, чтобы все они использовали одни и те же аргументы JVM. С помощью GRADLE_OPTS добиться этого невозможно. Поэтому была придумана новая системная переменная JDK_JAVA_OPTIONS, которая используется всеми JVM процессами в Gradle (подробней о ней здесь). Более того, начиная со сборки b157 появилась системная переменная JDK_JAVAC_OPTIONS для передачи параметров в Java компилятор (javac). Установим JDK_JAVA_OPTIONS в следующее значение:
И перезапустим сборку. У нас выпало исключение:
Такой ошибки у нас еще не было. Суть ее в том, что мы хотим получить доступ к классам из JDK, которые находятся в пакетах, не указанных модулем как экспортируемых (exported) в module-info.java. Это можно обойти, если добавить в JAVA_JDK_OPTIONS «—add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED»
Теперь сборка прошла успешно. Запустим тесты: gradle test. Тесты посыпались с теми же ошибками, что и в Maven. Это можно исправить, просто указав аргументы JVM в блоке test:
Мы все еще используем source/target compatibility 1.8 в нашем проекте. Если мы поменяем их на 1.9, то при компиляции получим ошибку:
Все потому что мы перешли на 1.9, а значит должны оформлять наши проекты в модули, писать module-info.java и в них указать, чтобы нам нужен (requires) тот или иной модуль из JDK. Ситуация становится интересней для Maven/Gradle зависимостей. Здесь можно прочесть о том, как конфигурировать модули, если они содержат одинаковые пакеты.
Однако пока Gradle еще не полностью готов к Java 9, вы можете запускать его под Java 8, а компилировать проект под Java 9. Для этого нужно в скрипте сборки указать конфигурацию под 9-ю версию:
Тестирование REST-контейнеров
Так как я в своем докладе рассказывал про REST-контейнеры и веб-сервера, то интересно будет их проверить на совместимость с Java 9, тем более что это достаточно сложные технологии.
Начнем с Ratpack. Это довольно новый сервер, полностью написанный на Java 8 и основанный на Netty. Если мы запустим его, то получим исключение:
Нам нужно лишь добавить модуль java.activation при старте сервера, чтобы это починить:
Теперь перейдем к Apache CXF. Для него нужно указать другой модуль:
Для запуска приложения с Jersey (JAX-RS) я использую Jetty сервер. Jersey тоже требует предыдущий модуль.
Я думаю, что вы знаете либо слышали, что летом этого года выходят Spring Framework 5 и Spring Boot 2.0, специально приуроченные к выходу JDK 9 и даже использующие некоторые ее возможности. Пока они не вышли, интересно проверить текущие версии (Spring Boot 1.5.3 и Spring Framework 4.3.8) на «запускаемость».
Spring Boot поддерживает три встроенных сервера: Tomcat, Jetty и Undertow. К сожалению, у меня не получилось запустить REST-приложение ни на одном из них. Оно просто зависало на стадии загрузки.
JMeter — одна из наиболее популярных утилит для тестирования веб-приложений. Она может работать как в консольном варианте, так и в режиме с UI. Готово ли оно к Java 9? Если мы возьмем последнюю версию (3.2) и запустим jmeter.bat, то увидим в консоли ошибку:
Это достаточно странное сообщение, которое судя по всему печатает не JVM. Если мы заглянем в исполняемый файл, то увидим целые блоки кода, где сначала выполняется команда «java-version», потом ее результат парсится и из него выделяется старшая и младшая версии. Причем JMeter предполагает, что результат будет в формате 1.8.0.
Так как в Java 9 результат уже в формате 9.0.0 (openjdk.java.net/jeps/223), то это приводит к катастрофическим последствиям и аварийному завершению приложения. Причем JMeter даже не пишет, что версия неправильная, а просто говорит, что не может найти Java.
Выход заключается в том, чтобы удалить все эти проверки и тогда утилита запустится корректно. Однако при загрузке конфигурации мы получим исключение:
Для того, чтобы избежать подобных ошибок, нужно лишь добавить в JVM аргументы в скрипте строку:
Заключение и спасительный флаг
Как вы видите, в последний год вендорами была проделана большая работа по обеспечению совместимости с Java 9. Это тем более было трудно, что даже в этом году были некоторые изменения в API JDK. Тем не менее многие библиотеки уже успешно работают над Java 9, некоторые без каких-либо ухищрений, некоторые с дополнительными аргументами для JVM.
Тем не менее, выяснять и разбираться в настройках не так приятно, поэтому в конце марта 2017 года руководство Oracle решило упростить жизнь разработчикам и добавить новый спасительный флаг, который упростит миграцию на Java 9, но будет удален в Java 10.
Этот флаг —permit-illegal-access разрешает доступ всем анонимным модулям через reflection к коду именованных модулей и позволяет не писать многочисленные —add-opens. Единственный минус этого флага — он будет выводить предупреждения в консоль обо всех случаях несанкционированного доступа.
Однако оказалось, что, хотя этот флаг и избавляет от необходимости указывать многочисленные —add-opens, он не избавляет от необходимости указывать самого себя. Поэтому начиная со сборки b172 его предполагается заменить на флаг —illegal-access. У этого флага будет четыре значения:
- permit. Это значение по умолчанию, которое открывает доступ через reflection из всех анонимных модулей во все пакеты именованных модулей. При этом значении только один раз печатается предупреждение о попытке доступа.
- warn. Каждая попытка получить доступ через reflection приводит к выводу предупреждения.
- debug. Здесь к выводу предупреждения выводится еще и stacktrace операции.
- deny. Планируется сделать дефолтным в Java 10. Запрещает доступ через reflection для всех пакетов в именованных модулях, кроме указанных явно в —add-opens.
Подробнее можно прочесть здесь. Интересно, что на момент сборки b173 этот флаг все еще не добавлен.
Я надеюсь, что вам было полезно это руководство и миграция ваших приложений будет более простой и быстрой.
Записки программера
Страницы
четверг, 20 июля 2017 г.
Руководство по Java 9: компиляция и запуск проекта
Команды java и javac редко используются Java-программистами. Такие инструменты, как Maven и Gradle делают их почти не нужными. Однако Maven и Gradle до сих пор не предоставляют полную поддержку для Java 9, поэтому, если вы хотите начать использовать её уже сейчас или просто хотите узнать некоторые полезные тонкости до официального релиза, стоит научиться вызывать java , javac и jar для управления своим кодом.
Статья призвана показать примеры использования этих команд, а также то, как эти команды изменились по сравнению с прошлыми версиями Java. Дополнительно будут рассмотрены новые инструменты: jdeps и jlink . Предполагается, что вы хоть немного знакомы с предыдущими версиями команд java / javac / jar и с модульной системой Java 9.
Установка Java 9
Сперва необходимо установить Java 9. Вы можете скачать её с сайта Oracle, но рекомендуется использовать SdkMAN!, так как в будущем он позволит вам с легкостью переключаться между разными версиями Java.
Можно установить SdkMAN! с помощью этой команды:
Посмотрите, какая сборка является последней:
Затем установите Java 9:
Теперь, если у вас установлены другие версии Java, вы можете переключаться между ними с помощью команды:
Компиляция и запуск «по-старому»
Для начала напишем какой-нибудь код, чтобы проверить наши инструменты. Если не использовать модульный дескриптор, то все выглядит так же, как и раньше.
Возьмем этот простой Java-класс:
Теперь, так как мы не использовали никаких особенностей Java 9, мы можем скомпилировать всё как обычно:
Команда создаст файл класса out/app/Main.class . Запустить его можно так же, как и в прошлых версиях:
Программа выведет Hello Java 9 .
Теперь создадим библиотеку Greeting также без особенностей Java 9, чтобы посмотреть, как это работает.
Создадим файл greeting/ser/lib/Greeting.java со следующим кодом:
Изменим класс Main для использования нашей библиотеки:
Скомпилируем эту библиотеку:
Чтобы показать, как работают оригинальные Java-библиотеки, мы превратим эту библиотеку в jar-файл без дескрипторов модулей Java 9:
Команда создаст файл libs/lib.jar , содержащий класс lib.Greeting .
Просмотреть информацию о jar-файле можно с помощью опции tf :
Команда должна вывести:
Теперь для компиляция app.Main нам необходимо указать компилятору, где найти класс lib.Greeting .
Используем для этого cp (classpath):
И то же самое для запуска программы:
Мы можем упаковать приложение в jar-файл:
И затем запустить его:
Вот так выглядит структура нашего проекта на данный момент:
Модуляризация проекта
Пока что ничего нового, но давайте начнем модуляризацию нашего проекта. Для этого создадим модульный дескриптор (всегда называется module-info.java и размещается в корневой директории src/ ):
Команда для компиляции модуля в Java 9 отличается от того, что мы видели раньше. Использование старой команды с добавлением модуля к списку файлов приводит к ошибке:
Чтобы понять, почему наш код не компилируется, необходимо понять, что такое безымянные модули.
Любой класс, который загружается не из именованного модуля, автоматически выполняет часть безымянного модуля. В примере выше перед созданием модульного дескриптора наш код не был частью какого-либо модуля, следовательно, он был ассоциирован с безымянным модулем. Безымянный модуль — это механизм совместимости. Проще говоря, это позволяет разработчику использовать в приложениях Java 9 код, который не был модуляризирован. По этой причине код, относящийся к безымянному модулю, имеет правила сродни Java 8 и ранее: он может видеть все пакеты, экспортируемые из других модулей, и все пакеты безымянного модуля.
Когда модульный дескриптор добавляется к модулю, его код больше не является частью безымянного модуля и не может видеть код других модулей, пока не импортирует их. В случае выше модуль com.app не требует никаких модулей, поэтому модуль библиотеки Greeting для него не виден. Он может видеть только пакеты модуля java.base .
Модули в Java 9, за исключением неуловимого безымянного модуля описанного выше, должны объявлять, какие другие модули им необходимы. В случае с модулем com.app единственным требованием является библиотека Greeting. Но, как вы могли догадаться, эта библиотека (как и другие библиотеки, не поддерживающие Java 9) не является модулем Java 9. Как же нам включить её в проект?
В таком случае вам нужно знать имя jar-файла. Если у вас есть зависимость от библиотеки, которая не была конвертирована в модуль Java 9, вам надо знать, какой jar-файл вызывается для этой библиотеки, потому что Java 9 переведёт имя файла в валидный модуль.
Это называется автоматический модуль.
Так же, как и безымянные модули, автоматические модули могут читать из других модулей, и все их пакеты являются экспортируемыми. Но, в отличие от безымянных модулей, на автоматические можно ссылаться из явных модулей.
Java 9 — Уже здесь!
На этой неделе, 21 сентября, наконец-то зарелизилась Девятка. Хех, Джава комьюнити не пошло на поводу у Майкрасофта и Эпла, которые решили пропустить из своих продуктов цифру 9 (привет, Винда и Айфоны) и всё-таки выпустило java 9.
Когда мы говорим про Java 8, думаю, все в первую очередь скажут про появление лямбд и стримов. Конечно, были и другие вещи, но именно эти фичи стали главными для релиза.
В Девятке есть лишь одна главная фича, которая, к сожалению, весьма спорна. Речь идёт про Project Jigsaw. Это нативная реализация модулей в платформе. Читать подробнее можно тут — http://openjdk.java.net/projects/jigsaw/.
Про модули пока не хочется говорить. Вся внутрення структура Джавы уже переписана на использование модулей. Но на сколько всё это дело взлетит — не ясно. Возможно, в пользовательском коде нам не придётся использовать jigsaw. Причин этому очень много. Во-первых, модули не решают jar hell problem. Во-вторых, пока модули никак не интегрированы с существующими подходами к дистрибьюции приложений — maven and gradle. Кроме того, совсем не ясно, какую проблему решают Модули для пользовательского кода, вне JDK.
Поэтому хочется обсудить вещи, пришедшие к нам с Девяткой, кроме Модулей. К сожалению, тут не так много всего вкусного и сахарного. Причём, многие вещи были выкинуты из Java 9, хотя должны были быть там (например, встроенный http client).
Хорошая статья про Сахар Девятки — https://habrahabr.ru/company/jugru/blog/336864/ В целом, там описаны все основные вещи, о которых нам стоит знать.
Продублирую самые интересные вещи для меня, которые бы я уже сейчас не прочь использовать. Хотя, надо признать, весь новый сахар давно был в Google Guava, который есть, наверное, в любом проекте. Поэтому, с точки зрения личного продуктивити, вряд ли что-то изменится в нашей жизни.
Билдеры для коллекций
Ура, теперь мы можем создавать Списки, Мапы и Сеты буквально в одну строку. И для этого нам не потребуется каких-либо либ. Выглядит всё это вот так вот:
Важно, что все эти коллекции — Незменяемы. Ничего добавлять или удалять из них нельзя — схватите UnsupportedOperationException.
Сахар для Optional
В классе java.util.Optional появилось 3 новых метода, которые драмматически улучшат вашу жизнь.
Первый — ifPresentOrElse.
Ничего хитрого. Раньше был просто метод ifPresent, который принимал Consumer, который применялся у хранящемосю в Опшенале значению. Теперь появилась возможность задать fallback.
Второй новый метод — or.
Тут тоже ничего хитрого. Либо мы возращаем наш существующий Опшенал, либо, если он отсутствует, продьюсируем новый и возвращаем его.
Третий новый метод — stream.
Этот метод можно использовать, как пишут создатели языка, для того, чтобы из Стрима Опешанолов, получить Стрим реально представленных значений.
В общем, перед нами вполне полезные методы для работы с Монадой Мейби. Всё это можно было сделать и раньше, но теперь — это стало удобнее.
HTTP/2 Client
Другое, о чём бы я хотел поговорить в свете Девятки — это новый, кленовый HTTP-клиент. По суте, полная копия Apache HttpClient. Кажется, что перед нами действительно довольно гибкий клиент, позволяющий настроить буквально всё. Http 1.1, Http 2, авторизация, куки, синрохронность и асинхронность, настройка пула потоков (экзекьютор), хэдэра — да, всё это должно было быть в HTTP клиенте джавы.
К сожалению, Клиент был в последний момент вынесен из JDK 9 в инкубатор — место, в котором теперь разрабатываются все новые фичи Джавы. Однако есть и хорошие новости — нам обещают завести клиент в Десятке.
Для того чтобы уже сейчас, в Java 9, попробовать клиент, требуется сделать несколько вещей. Во-первых, вы должны подключить в зависимости своего модуля — модуль, в котором как раз таки и реализован клиент. Для этого в файле module-info.java, нужно написать что-то типа того:
Где вместо ru.hixon — вы должны задать имя пакета, в котором хотите использовать Клиент из Инкубатора. После этого вы можете использовать все новые классы для Клиента.
Самый простой GET запрос можно сделать примерно так:
Асинхронный вариант этого куда такой же простой:
В Общем, перед нами вполне удобный HTTP-клиент. Пока он не продакшен-реди — чего только стоит закомментированный код в исходниках клиента в релизной JDK).
Будем надеяться, что уже скоро мы получим вполне годную вещь. А сейчас продолжим использовать клиенты Апача, Спринга и иже с ними.
Другие изменения Java 9
Как я выше уже сказал, на хабре есть прекрасная статья, которая описывает всё это. Но ещё раз однословно расскажу, что появилось нового — мало кто из вас забанен на хабре.
Улучение Стримов — доработана работа с Optional. Добавлены популярные ранее в других языках программирования конструкции takeWhile и dropWhile, которые позволяют не обрабатывать часть стрима по некоторому условию.
Улучшение CompletableFuture. Добавлен метод copy, который позволяет полностью скопировать Фьючу. Самое главное — завершение скопированной фьючи никак не отражается на прородители. Ещё важно, что завершение Фьючи-родителя завершит автоматически всех детей, даже скопированных.
Появился API для работы с процессами операционной системы — класс ProcessHandle. Теперь можно легко, а самое главное — кросплатформерно — работать с сущностью Процесса, получая всю нужную информацию про него.
Очень важная вещь — появление класса StackWalker, который позволяет работать со стектрейсами, не создавая объектов Exception. Я думаю, что все понимают, почему это важно. Создание объектов исключения — это дорогая операция. Таким образом, туллинг, которому приходится анализировать стэктрейсы, теперь может здорово ускорится, если его переписать с использованием нового API.
Супер противоречивая вещь — появилась возможность реализовывать приватные методы в интерфейсах. Думаю, все знают Парадокс разбитых окон. Перед нами — отличный пример этого парадокса. Стоило в Java 8 придумать костыль, херовое архитектурное решение (с дефолтными методами), как в Java 9 мы уже вынуждены придумывать ещё более херовое решение, которое нужно нам для поддержки кодобазы.
Подводя итоги. Девятка — не такая плохая, чтобы на неё не переходить. Однако пока совсем не понятно, что будет в ситуации с моделями, старым кодом, Unsafe и вот этим всем. Поэтому, можно предположить, что скорость адоптации новой версии Джавы будет, к сожалению, меньше чем в восьмёрке. Да и sales points у данного релиза — нууу, такой себе.
Java Junior. Какой набор знаний для вхождения в профессию?
Приветствую честной народ!
Интересует информация о минимальном багаже знаний для вхождения в профессию в целом и некие рекомендации в частности.
На хабре и подобных ресурсах такие темы 3+ годичной давности, интересует же актуальная информация.
1. Какой набор знаний и технологий требуется?
Я так понимаю, что в основном разработка сейчас на JavaEE, с использованием Spring+hibernate+SQL+регулярные выражения? Remote Method Invoccation, потоки насколько глубоко требуется знать?
Также надо знать git или его можно изучить уже по факту? Регламентируется ли в кампаниях, какую IDE использовать – например, если привык к Intellij Idea, потребуется ли переучиваться на Eclipse?
Насколько глубоко требуется знание SQL?
2. Что почитать из комплексного, особенно по проектированию приложения, разработке классов и их структуры?
Сейчас читаю: Java APIs, Extensions and Libraries, 2nd Edition (2018).
3. Если есть кто из HR или видно по своим коллективам: какой возраст у соискателей на Junior позиции, после 30 реально? а после 40?)))
1 – не путай JavaEE и Spring =) хотя в в целом да, подавляющее большинство разработки на Java – это корпоративные Web приложения, и повсеместно встречаются Spring + Hibernate, а дальше уже вариации. SQL знать желательно, но не обязательно, ибо тот же Hibernate из коробки делает все за тебя. Регулярки – вкусовщина, я на своих проектах категорически против регекспа но иногда без него никуда. С многопоточкой ни разу за 5 лет не работал в том виде, в котором ее нужно было бы хоть сколько-то серьезно знать, ну не довелось.
Гит – сейчас подавляющее большинство работает в умной IDE а там все возможности гита реализованы “из коробки”, запушить сможешь нажатием 1 кнопки, а резолвить мержхелл – не твоя работа на данном этапе.
про иде – чаще всего нет, но кое-где, могут потребовать не использовать ломанного ПО, при этом покупать тебе ИДЕ не захотят, по этому будь готов либо купить сам, либо поработать в Eclipse | NetBeans
SQL как я говорил выше – это дополнительные знания, которые послужат приятным бонусом, нежели каким-то обязательным параметром.
КМК важнее
1) изучить 10-пальцевую печать, и поднять скорость где-то до 200 символов в минуту на английском
2) понять, в общих чертах, как вобще работает Java машина.
3) изучить алгоритмы и структуры данных опять же на базовом уровне, что бы просто понимать, как это усутроено
4) фреймворки изучаются достаточно быстро, и начать джуну писать на том же спринге – дело 2-х недель. Сразу оговорюсь – джун не отвечает за свой код, по этому он и джун.
2. Что почитать из комплексного, особенно по проектированию приложения, разработке классов и их структуры?
Сейчас читаю: Java APIs, Extensions and Libraries, 2nd Edition (2018).
почитаешь это лет через 5-6, когда станешь архитектором =))
сейчас почитай допустим Хорстмана (Библиотека профессионала), Блоха (Эффективное программирование) Шеффер (Spring для профессионалов) этого тебе хватит за глаза для того, что бы устроиться и начать работать.
3. Если есть кто из HR или видно по своим коллективам: какой возраст у соискателей на Junior позиции, после 30 реально? а после 40?)))
я не hr но провел более 200 технических собеседований, за последние пару лет. Джуны после 30 – редкость, но не исключение. К слову в Java я сам пришел в 32, сейчас 37.