С постоянными обновлениями и улучшениями Java, разработчики сталкиваются с необходимостью рефакторинга кода — изменением внутренней структуры программы. Для достижения целей рефакторинга, например, может потребоваться переход с Java 8 на Java 17. В статье будут рассмотрены вопросы такой миграции, побудительные причины, механизмы решения и иные, обусловливающие этот процесс, обстоятельства.
Java 8 была выпущена 18 марта 2014 г. В январе 2019 г. закончилась общедоступное обновление Oracle Java SE 8 для коммерческих пользователей, что повлекло за собой прекращение обновлений функций безопасности и производительности [2].
По состоянию на 2023г. доля приложений, написанных на языке Java 8, составляет 33 % и занимает второе место в мире среди Java — приложений. (46 % — в 2022г.)
В сентябре 2021 г. была выпущена Java 17. Более 9 % Java — приложений в настоящее время функционируют на Java 17 (по сравнению с менее чем 1 % в 2022 г.), что соответствует темпам роста на 430 % в течение одного года [3].
Изменение определенной функциональности с приходом новых версий Java приносит пользу разработчикам как прямо, так и косвенно, поскольку она обеспечивает масштабируемость платформы, ее целостность и улучшает производительность.
Используя Java 8 для разработки приложений, следует учитывать серьезные недостатки этой версии платформы.
В Java 8 отсутствуют возможности для правильной поддержки модульности между артефактами, и, хотя это вызывает проблемы, их, очевидно, можно решить. Но, когда наконец они проявляются, обычно в более крупных приложениях, с ними практически нельзя справиться. Сложности, с максимальной вероятностью влияющие на процесс разработки, объединены под общим названием «JAR-ад». Чтобы приложение работало, ему может понадобиться сразу несколько библиотек. Но каждая из них может потребовать еще несколько и т. д. По мере усугубления проблемы невыраженных зависимостей проект становится все более громоздким и подверженным ошибкам.
Иногда случается так, что разные JAR-файлы в пути класса имеют одинаковые полные имена. Это происходит по целому ряду причин. Поскольку класс будет загружен из первого JAR-файла в пути класса, который его содержит, все другие классы с такими же именами станут недоступными — это значит, что они затенены. Затенение чаще всего происходит случайно. Хотя инструменты сборки могут снизить вероятность случайного затенения, обычно им не под силу предотвратить его.
Конфликты версий — проклятие любого большого программного проекта. Когда количество зависимостей обозначается больше, чем одной цифрой, вероятность возникновения конфликтов стремится к 1 с ужасающей скоростью. Если обе версии находятся в одном и том же пути класса, то их поведение становится непредсказуемым.
Загрузчик класса превращает все загружаемые классы в один большой комок грязи (программная система с нераспознаваемой архитектурой) — так, что все публичные классы доступны для всех остальных. Из-за такой слабой инкапсуляции становится невозможным создать функциональность, которая будет видна внутри JAR, но не снаружи.
Низкая производительность при загрузке. Одна из причин — загрузчик класса не может заранее знать, из какого JAR-файла будет загружен класс, поэтому линейно проходит по всем JAR в пути класса. [1, с. 44]
Указанные проблемы версии Java 8 помимо прочего влекут за собой значительные ограничения для разработки архитектуры крупных программных систем. Таким образом существуют объективные причины для перехода на так называемую модульную платформу.
Так, в сентябре 2017г. появился Project Jigsaw — модульный проект. Основными целями этого проекта были:
1) облегчить разработчикам создание и обслуживание библиотек и больших приложений;
2) улучшить безопасность и удобство обслуживания платформы Java SE в целом и JDK в частности;
3) улучшить производительность приложений;
4) обеспечить масштабирование платформы Java SE и JDK для использования на небольших вычислительных устройствах и облачных средах.
Для достижения этих целей была разработана и внедрена стандартная модульная система для платформы Java SE, а также применена к самой платформе и к ее эталонной реализации JDK, начиная с версии 9 [4].
Модуль можно рассматривать как фундаментально новый тип программного компонента Java. Это именованный набор кода и данных с собственным описанием. Его код организован как набор пакетов, содержащих типы, т. е. классы и интерфейсы Java, его данные включают ресурсы и другие виды статической информации.
Корпорация Oracle разделила файлы JDK и спецификации Java SE на два набора модулей:
1) все модули JDK начинаются с «jdk. *».
2) все модули спецификаций Java SE начинаются с «java. *».
Использование, в частности, модульной системы в Java 17 предоставляет существенные преимущества по сравнению с Java 8.
Программируя на Java 17, имеется возможность указать только необходимые модули, поскольку все API-интерфейсы платформы разделены на отдельные модули. Такая возможность существенно сокращает объем приложения, улучшает его безопасность за счет отсутствия большого количества классов, включенных в дистрибутив, как это было ранее в Java 8.
Исполняющая среда может проверять весь граф зависимостей модульной системы от модулей приложения при ее запуске. Если какие-либо обязательные не найдены, виртуальная машина сообщает об этом и завершает работу [5].
Теперь за счет модульности зависимости имеют строгую иерархию и в конце концов все ссылаются на java. base — базовый модуль, который включает в себя все базовые библиотеки.
При разработке модульных приложений на Java 17 необходимо пользоваться понятием «Modulepath». Код, написанные без использования модулей, может быть и далее расположен по пути к классам. Этот код загружается в безымянный модуль, который имеет особое назначение и может получить доступ ко всем остальным модулям, входящим в состав модуля «java.se». Безымянный модуль применяется автоматически, когда файлы классов размещаются по пути к классам.
Следует также учитывать механизм рефлексии для доступа к классам при разработке приложений, поскольку большинство библиотек в экосистеме Java основываются на ней. Объявление модуля открытым с помощью модификатора доступа влечет за собой доступ ко всем пакетам в таком модуле. Однако существует ключевое слово «opens», позволяющее выборочно открывать пакеты для рефлективного доступа.
В состав модульной системы входит механизм служб, предназначенный разрешать проблемы, обусловленные инкапсуляцией.
Пользователям инструментального средства Maven следует помнить, что оно не является модульной системой, однако развивается в сторону полной интеграции с ней.
Разработчик может воспользоваться следующими инструментами для анализа кода и упрощения задачи по переходу на модули:
1) jdeps — помогает анализировать кодовую базу для выявления зависимостей от JDK API и сторонних JAR-файлов. Также упоминается название модуля, в котором можно найти JDK API. Это упрощает модульность кодовой базы;
2) jdeprscan — помогает анализировать кодовую базу на наличие устаревших API;
3) jlink — помогает создать меньшую среду выполнения за счет объединения модулей приложения и JDK;
4) jmod — помогает в работе с файлами jmod. jmod — это новый формат упаковки модулей. Этот формат позволяет включать собственный код, файлы конфигурации и другие данные, которые не помещаются в файлы JAR.
Кратко рассмотрев проблемы Java 8, аспекты перехода на Java 17 и связанные с этим процессом особенности можно заключить следующее.
Модульные средства позволяют сократить время запуска прикладных программ, объем занимаемой памяти и сложность путем отказа в доступе к внутренним элементам. Разработчикам следует рассматривать возможность проектирования приложений модульным способом даже несмотря на то, что он имеет достаточно много аспектов, поскольку большинство из них не потребуется. Основные концепции объявления модулей, модульных JAR-файлов, modulpath, безымянных модулей, автоматических модулей и модульных служб станут в достаточной степени изученными большинством разработчиков Java в ближайшее время.
Литература:
- Парлог, Н. Система модулей Java / Н. Парлог. — 1. — Санкт-Петербург: Питер, 2021. — 464 c. — Текст: непосредственный.
- Donald, Smith End of Public Updates is a Process, not an Event / Smith Donald. — Текст: электронный // Oracle: [сайт]. — URL: https://blogs.oracle.com/java/post/end-of-public-updates-is-a-process-not-an-event (дата обращения: 06.08.2023).
- 2023 State of the Java Ecosystem. — Текст: электронный // newrelic.com: [сайт]. — URL: https://newrelic.com/resources/report/2023-state-of-the-java-ecosystem (дата обращения: 06.08.2023).
- Project Jigsaw. — Текст: электронный // openjdk.org: [сайт]. — URL: https://openjdk.org/projects/jigsaw/ (дата обращения: 06.08.2023).
- Модули Java с примерами. — Текст: электронный // java-blog.ru: [сайт]. — URL: https://java-blog.ru/osnovy/moduli-v-java (дата обращения: 06.08.2023).