Сандер Мак о переходе к Java-модулям

Вышла Java 10 — значит, пришло время использовать Java 9! Шутки шутками, но с неторопливостью индустрии сейчас действительно впору обсуждать модули Jigsaw: шумиха вокруг них была в прошлом году, а вот по-настоящему многие задумаются уже в этом.

В новом выпуске подкаста InfoQ на вопросы о модулях ответил Сандер Мак — соавтор книги «Java 9 modularity», который на этой теме печеньку съел. А у нас на конференции JPoint он как раз выступит с темой «Designing for modularity with Java modules». И мы решили перевести на русский часть сказанного в подкасте: если вы не следили пристально за модулями, поможет быстро понять главное, а если сами будете на JPoint — станет «предисловием» к докладу.

— Java-разработчиков в мире порядка шести миллионов, но далеко не все они используют Java 9. Судя по вашим наблюдениям, насколько активно люди переходят на неё?

— Да, думаю, можно без особого риска утверждать, что из этих шести миллионов на «девятке» сейчас меньшинство. На Java 8 люди прыгали довольно быстро, потому что видели лямбды и стримы, а это очень впечатляющие и убедительные фичи, которые можно просто брать и использовать. А с Java 9 ситуация другая. Мало того, что её пришлось долго ждать, так ещё и главная фича в виде модульной системы — совсем не такая, как лямбды и стримы. Она больше про архитектуру и проектирование приложений, она влияет на используемые инструменты — в общем, это фича, которой требуется куда больше времени.

Поэтому меня не удивляет, что люди не прыгают на Java 9 так, как переходили на Java 8. Ну и ещё сказывается то, что Oracle решили изменить график выхода версий на полугодовой, так что Java 9 уже оказывается сменена Java 10. Комьюнити ещё должно принять эту новую схему, командам и людям надо разобраться, как со всем этим быть, когда уже в сентябре Java 11 выйдет. По-моему, из-за этого люди пока что заняли выжидательную позицию.

И это печально, потому что изменения между Java 8 и 9 довольно большие — и из-за модуляризации, и из-за кучи других фич, это большой релиз. Если вы до сих пор не разбираетесь с ним, по-моему, вы оказываете себе медвежью услугу. Потому что, когда вы будете-таки переходить на новую версию — будь то хоть 9, хоть 10, хоть 11 — вы всё равно столкнётесь с теми же проблемами, с которыми можете разобраться уже сейчас.

— Хорошее замечание. К словам о том, как лямбды и стримы манили разработчиков: а в случае с модуляризацией в чём вообще главная выгода? Она явно делает всё меньше, но почему это так важно?

— Ну, я не думаю, что главное в ней «делать всё меньше», я думаю, что главное — делать всё более управляемым и поддерживаемым. Модуляризация — это, по сути, архитектурный принцип, которого стоит придерживаться при работе с большими кодовыми базами. Обычно, когда команда из нескольких разработчиков начинает новый проект, стартуя с чистого листа, поначалу всё хорошо и фичи реализуются одна за другой, но через какое-то время упираешься в стену. Кодовая база становится гораздо сложнее — возможно, через год, или через два, или через четыре, но настаёт момент, когда её всё сложнее и сложнее поддерживать, добавлять новые фичи без того, чтобы где-то что-то сломалось. И в этот момент корректно модуляризовать её уже сложно.

Конечно, у Java-разработчиков и раньше были инструменты для такого. При их грамотном применении и при осознании стоящих за ними принципов можно было добиться многого. Но теперь есть «родная» модульная система, помогающая инкапсулировать код, а инкапсуляция тут важна. Если задуматься о модуляризации — она о том, чтобы прятать вещи. Если вы прячете детали реализации и выставляете наружу только API, это очень помогает быть уверенным, например, что никто не будет использовать «внутренности» неправильно. Кроме того, у модулей есть явно прописанные зависимости, они напрямую сообщают, какие ещё модули им нужны, и это знание может быть использовано тулингом — например, компилятором или рантаймом — чтобы убедиться, что всё на месте.

И это большой шаг вперёд по сравнению с привычным classpath. У JAR-файлов в classpath нет информации о зависимостях, так что можно упустить нужный файл, и это даст о себе знать уже в рантайме. А кроме того, любой публичный класс в JAR-файле виден любому другому публичному классу в classpath. Так что модуляризация и привносит инкапсуляцию, и помогает делать более поддерживаемый и надёжный софт.

— Но то, о чём вы говорите, звучит подходящим для новых проектов. А если сейчас уже есть запущенная на «восьмёрке» система, то выгода от её перехода на «девятку» неочевидна. Что могу выиграть? Будет ли быстрее запуск? Будет ли меньше поверхность атаки, раз теперь можно не включать неиспользуемые вещи вроде CORBA?

— Определённо, это больше подходит новым проектам, а мигрировать большую кодовую базу на модули зачастую сложно. Но с переходом на Java 9 даже без модулей получаешь некоторые преимущества. Например, сборщиком мусора по умолчанию стал G1GC, в котором много улучшений, и есть Compact Strings — если в вашем коде много ASCII-строк, увидите разницу.

Кроме того, как вы уже упомянули, поскольку теперь сам JDK модуляризован, и традиционно входившие в состав JDK вещи вроде CORBA получили статус deprecated и deprecated for removal («будет удалено в следующих версиях»), а кое-что вообще удалено (впервые за долгое время). Но тут есть и обратная сторона: если ваше приложение такие штуки использует, то у вас возникает проблема.

— Про «вообще удалено» — что именно, и почему Oracle именно в этой версии решились что-то удалять?

— Если правильно помню, удалили шесть методов, связанных с PropertyChangeListener. И это связано с тем, что они — часть JavaBeans, а JavaBeans попали в модуль java.desktop. Что довольно странно, но это легаси, с которым нам жить. И при этом, например, у java.util.logging.LogManager был метод AddPropertyChangeListener. Что нормально работает, когда всё в classpath — кому какое дело. Но всё перестаёт быть нормальным, когда у LogManager в JDK зависимость от десктопного модуля: он довольно большой и посвящён вещам вроде AWT и Swing. Так что команда решила удалить методы, чтобы избежать такого.

Я думаю, это хорошо показывает трейд-оффы при переходе к модулям. В вашей кодовой базе могут быть такие заковыристые зависимости, и всё работает, если просто закидывать всё в classpath. Но можно ли назвать хорошим дизайном, когда у вас реализация логирования зависит от десктопного API? Такими вопросами стоит задаваться, вот они при разработке Java 9 и задались. Думаю, при переходе к модулям у людей всплывёт много таких скрытных и на самом деле не нужных зависимостей, указывая на запутанные места, которые вообще-то стоит распутать.

— Что вы сейчас видите на практике: когда люди с существующим проектом переходят с Java 8 на 9, они остаются с classpath, или идут до конца и модуляризуют приложение?

— Я в основном вижу остающихся с classpath. Потому что это привычно людям, они это знают, и даже с этим у них в некоторых случаях возникают сложности. Если у вас приложение на Java 8, то при переходе на 9 даже без модулей будьте готовы к возможным проблемам. Например, если у вас ваш код использует API из JDK, которые теперь оказались инкапсулированы. Вопросы есть, но они решаемые.

Но, конечно, когда уже перешёл на Java 9 с classpath, то следующим вопросом становится «переходить ли теперь на модули». Нет какого-то подходящего всем сразу ответа на это. Если ваша кодовая база сама по себе уже имеет довольно модульную структуру, то использовать Java-модули вам будет и удобно, и выгодно. Но если у вас один большой JAR со спагетти-кодом, то вам перейти к модулям будет непросто. Модули — это не какая-то магическая языковая фича, где просто вставляешь несколько ключевых слов. Это сложная задача разбиения вашего кода на поддающиеся управлению независимые части. И никакая фича за вас эту работу не проделает. В общем, тут надо смотреть по ситуации и решать в каждом конкретном случае отдельно.

— Вместе с Java 9 появился ещё и инструмент jlink — можете о нём рассказать?

— Да, это действительно новый инструмент из JDK 9, и я думаю, что это очень клёвое применение модульной системы. Ситуация такая: с jlink можно взять своё модульное приложение, указать его корневой модуль, и сказать jlink создать образ рантайма под вас. Это практически кастомная JRE — содержит ваши модули, а также те и только те модули из JDK, которые нужны для запуска вашему приложению. Это, конечно, основано на метаданных, на модульных дескрипторах из вашего приложения.

Если взять для примера десктопное приложение, оно может ограничиваться модулем java.desktop, зависимостями этого модуля и кодом самого приложения. А всякого ненужного старья вроде CORBA там не будет. Если взять совсем простой Java-класс, использующий только модуль java.base (корневой модуль для JDK), тогда весь образ рантайма получится около 20 мегабайт — то есть гораздо меньше, чем рапространять с приложением JDK целиком. И ещё в этом процессе ко всем модулям могут применяться оптимизации, например, создаваться кэш всех ваших модульных дескрипторов, чтобы не приходилось парсить их при запуске.

— Выход Jigsaw в своё время оказался отложен отчасти из-за вопросов совместимости с другими модульными системами вроде OSGi и JBoss Modules. Что с этим теперь?

— На Java 9 можно запускать OSGi-модули, используя classpath, можно использовать модули JBoss. Но если хочется пойти дальше, если задумываешься о взаимодействии OSGi и Java-модулей — ну, были эксперименты, в основном со стороны OSGi Alliance, но ничего подходящего для продакшна. Пока что у Jigsaw и OSGi нет никакого реального интеропа. Они просто сосуществуют в одной сфере.

Так что, если у вас уже есть OSGi-приложение, вряд ли вы будете переписывать его на Java-модули, просто будете использовать classpath. А если начинаете с нуля новое приложение, тогда у вас есть выбор. Что стоит предпочесть, зависит от того, что вам нужно. Если хочется чего-то родного для платформы, понятно, что напрашиваются Java-модули. Но вы можете хотеть и динамики OSGi, там есть вещи, которые будет непросто реализовать поверх Java-модулей. (примечание JUG.ru: если вы столкнулись с выбором из Jigsaw и OSGi, вас может заинтересовать видеозапись доклада Никиты Липского)

— Чего ожидаете от модульной системы в будущем, уже за пределами «девятки»?

— В данный момент, насколько мне известно, нет открытых JEP, связанных с модульной системой. Это не значит, что никакой работы не происходит, но в основном косметическая. Но чего я лично жду от следующих итераций модульной системы, так это версионирования.

Сейчас модульная система может фиксировать версии модулей, но не использует это для проверок или разрешений, связанных с версиями. И для многих это звучит удивительно, потому что большинство модульных систем этим занимаются. Но при работе над Jigsaw это намеренно пометили «out of scope». У нас уже есть системы сборки вроде Maven и Gradle, есть другие модульные системы вроде OSGi, и все они делают что-то с версиями, но у всех разная семантика, разные механизмы разрешений и так далее. Просто брать и сразу помещать такое в JDK и в спецификацию языка не захотели, и я могу это понять. Но в то же время версионирование и модуляризация логично идут рука об руку, и я надеюсь, что это станет частью модульной системы в Java.

Послушать выпуск подкаста целиком можно на SoundCloud, а увидеть доклад Сандера о модулях — 7 апреля на конференции JPoint.