Зачем нужен проект Jigsaw / JPMS?

79

Система управления пакетами Java всегда казалась мне простой и эффективной. Он активно используется самим JDK. Мы использовали его для имитации концепции пространств имен и модулей.

Что пытается заполнить Project Jigsaw (он же система модулей платформы Java )?

С официального сайта:

Цель этого проекта - разработать и реализовать стандартную модульную систему для платформы Java SE, а также применить эту систему к самой платформе и к JDK.

Джон
источник

Ответы:

100

Jigsaw и OSGi пытаются решить одну и ту же проблему: как позволить более крупнозернистым модулям взаимодействовать, при этом экранируя их внутренние компоненты.

В случае с Jigsaw более грубые модули включают классы Java, пакеты и их зависимости.

Вот пример: Spring и Hibernate. Оба имеют зависимость от стороннего JAR CGLIB, но они используют разные несовместимые версии этого JAR. Что делать, если полагаться на стандартный JDK? В том числе версия, которую хочет Spring, ломает Hibernate и наоборот.

Но если у вас есть модель более высокого уровня, такая как Jigsaw, вы можете легко управлять разными версиями JAR в разных модулях. Думайте о них как о пакетах более высокого уровня.

Если вы соберете Spring из исходного кода GitHub, вы тоже его увидите. Они переделали фреймворк, так что он состоит из нескольких модулей: ядра, постоянства и т. Д. Вы можете выбрать минимальный набор зависимостей модулей, который нужен вашему приложению, и игнорировать остальные. Раньше это был один Spring JAR со всеми файлами .class.

Обновление: пять лет спустя - возможно, у Jigsaw все еще есть проблемы, которые нужно решить.

Duffymo
источник
5
Что, если вам все еще нужно использовать один и тот же модуль, но две его разные версии? Разве они не должны просто добавить какую-то поддержку, чтобы две версии одних и тех же классов могли сосуществовать?
Дидье А.
7
этот пост вводит в заблуждение, учитывая то, что на самом деле планируется выпустить в java 9. Возможно, он был точным на момент написания.
xenoterracide
1
mreinhold.org/blog/jigsaw-complete проект завершен и выпущено для Java 9
Zasz
@xenoterracide, нельзя винить кого-то в том, что он не ясновидящий. Сообщение предшествовало Java 9 на пять лет. Вы также просматриваете каждый ответ Джона Скита?
duffymo
Этот пост не устарел. Модули Java намеренно не решают проблему управления версиями, см. Эту ветку . До сих пор нет простого способа обойти наших старых друзей NoSuchMethodErrorи NoClassDefFoundError.
Tamas Hegedus 01
45

AFAIK План состоит в том, чтобы сделать JRE более модульным. Т.е. есть банки меньшего размера, которые не являются обязательными, и / или вы можете загружать / обновлять только те функции, которые вам нужны.

Это сделано для того, чтобы сделать его менее раздутым и дать вам возможность отказаться от устаревших модулей, которые, возможно, большинство людей не используют.

Питер Лоури
источник
7
Принятый ответ действителен, но этот ответ лучше, потому что он объясняет фактические желаемые эффекты. +1, заслуженно.
Silviu Burcea
Мне любопытно, значит ли это также, что если у меня есть Google Guava в качестве зависимости, но я использую в нем только ImmutableList, тогда я могу импортировать только зависимости ImmutableList и оставить остальные классы Guava вне?
tmn
1
@ThomasN. когда вы используете что-то, importэто только вводит этот класс в пространство имен класса для компилятора. Если вы на самом деле его не используете, он не будет отображаться в созданном байтовом коде. Если вы действительно используете этот класс, вам нужен этот класс и каждый класс, который он использует во время выполнения. Теоретически вы можете создать урезанную версию API guava, в которой есть только то, что вам нужно, и использовать вместо этого этот JAR. На самом деле это подвержено ошибкам и в большинстве случаев не очень полезно, и вы просто добавляете весь JAR в том виде, в котором он был выпущен.
Питер Лоури
43

На основе Mark Reinhold «s программной речи на Devoxx Бельгии , проект Jigsaw собирается решить две основные болевые точки:

  1. Путь к классам
  2. Массивный монолитный JDK

Что не так с Classpath?

Все мы знаем о JAR Hell . Этот термин описывает все различные способы, при которых процесс загрузки классов может перестать работать. Наиболее известные ограничения пути к классам:

  • Трудно сказать, есть ли конфликты. Инструменты сборки, такие как maven, могут неплохо справляться с именами артефактов, но если сами артефакты имеют разные имена, но одинаковое содержимое, может возникнуть конфликт.
  • Основная проблема с файлами jar заключается в том, что они не являются компонентами. Это просто набор файловых контейнеров, поиск по которым будет выполняться линейно. Путь к классам - это способ поиска классов независимо от того, в каких компонентах они находятся, в каких пакетах они находятся или их предполагаемом использовании.

Массивный монолитный JDK

Большая монолитность JDK вызывает несколько проблем:

  • Он не подходит для небольших устройств. Несмотря на то, что небольшие устройства типа IoT имеют процессоры, способные запускать виртуальную машину класса SE, у них не обязательно есть память для хранения всего JDK, особенно когда приложение использует только небольшую ее часть.
  • Это даже проблема в облаке. Облако - это оптимизация использования оборудования. Если у вас есть тысячи образов, содержащих весь JDK, но приложения используют только небольшую его часть, это будет пустой тратой.

Модули: общее решение

Для решения вышеуказанных проблем мы рассматриваем модули как фундаментальный новый вид программных компонентов Java. Модуль - это именованный набор кода и данных с самоописанием. Его код организован как набор пакетов, содержащих типы, т. Е. Классы и интерфейсы Java; его данные включают ресурсы и другую статическую информацию.

Чтобы контролировать, как его код относится к типам в других модулях, модуль объявляет, какие другие модули ему требуются для компиляции и запуска. Чтобы контролировать, как код в других модулях ссылается на типы в своих пакетах, модуль объявляет, какой из этих пакетов он экспортирует.

Модульная система находит необходимые модули и, в отличие от механизма пути к классам, гарантирует, что код в модуле может ссылаться только на типы в модулях, от которых он зависит. Механизмы управления доступом языка Java и виртуальной машины Java предотвращают доступ кода к типам в пакетах, которые не экспортируются их определяющими модулями.

Помимо большей надежности, модульность может улучшить производительность. Когда код в модуле ссылается на тип в пакете, то этот пакет гарантированно будет определен либо в этом модуле, либо точно в одном из модулей, читаемых этим модулем. Следовательно, при поиске определения определенного типа нет необходимости искать его в нескольких модулях или, что еще хуже, по всему пути класса.

JEPs, чтобы следовать

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

  • JEP 200: Модульный JDK : используйте Систему модулей платформы Java (JPMS) для создания модулей JDK
  • JEP 201: Модульный исходный код : реорганизовать исходный код JDK в модули, улучшить систему сборки для компиляции модулей и обеспечить соблюдение границ модуля во время сборки
  • JEP 261: Модульная система : реализация модульной системы платформы Java, как указано в JSR 376, вместе с соответствующими изменениями и улучшениями, специфичными для JDK.
  • JEP 220: Модульные образы времени выполнения : реструктуризация образов времени выполнения JDK и JRE для размещения модулей и повышения производительности, безопасности и удобства обслуживания.
  • JEP 260: инкапсулировать большинство внутренних API-интерфейсов : сделать большинство внутренних API-интерфейсов JDK недоступными по умолчанию, но оставить доступными несколько важных, широко используемых внутренних API-интерфейсов до тех пор, пока не появятся поддерживаемые замены для всех или большей части их функций.
  • JEP 282: jlink: компоновщик Java : создание инструмента, который может собирать и оптимизировать набор модулей и их зависимости в настраиваемый образ времени выполнения, как определено в JEP 220.

Заключительные замечания

В первом выпуске отчета «Состояние модульной системы» Марк Рейнхольд описывает конкретные цели модульной системы следующим образом:

  • Надежная конфигурация для замены хрупкого, подверженного ошибкам механизма пути к классам средством, позволяющим программным компонентам объявлять явную зависимость друг от друга, а также
  • Сильная инкапсуляция , позволяющая компоненту объявлять, какие из его общедоступных типов доступны другим компонентам, а какие нет.

Эти функции принесут пользу разработчикам приложений, разработчикам библиотек и разработчикам самой платформы Java SE напрямую, а также косвенно, поскольку они обеспечат масштабируемую платформу, большую целостность платформы и улучшенную производительность.

Али Дехгани
источник
3
Марк Рейнхольд - главный архитектор группы платформ Java в Oracle, и этот ответ, по сути, является его прямым ответом на этот точный вопрос.
Джей
1
Чтобы вы могли выразить это количественно, HelloWorld может использовать 15 МБ вместо 553 МБ; youtu.be/rFhhLXcOBsk?t=31m12s
user1133275
14

В качестве аргумента давайте предположим, что Java 8 (и более ранние версии) уже имеет «форму» модулей (jar-файлов) и модульную систему (путь к классам). Но с этим есть известные проблемы.

Изучая проблемы, мы можем проиллюстрировать мотивацию Jigsaw. (Ниже предполагается, что мы не используем OSGi, JBoss Modules и т. Д., Которые, безусловно, предлагают решения.)

Проблема 1: публично слишком публично

Рассмотрим следующие классы (предположим, что оба являются общедоступными):

com.acme.foo.db.api.UserDao
com.acme.foo.db.impl.UserDaoImpl

В Foo.com мы можем решить, что наша команда должна использовать, UserDaoа не использовать UserDaoImplнапрямую. Однако нет никакого способа принудительно применить это в пути к классам.

В Jigsaw модуль содержит module-info.javaфайл, который позволяет нам явно указывать, что является общедоступным для других модулей. То есть у публики есть нюанс. Например:

// com.acme.foo.db.api.UserDao is accessible, but
// com.acme.foo.db.impl.UserDaoImpl is not 
module com.acme.foo.db {
    exports com.acme.foo.db.api;
}

Проблема 2: отражение необузданно

Учитывая классы в # 1, кто-то все еще может сделать это в Java 8:

Class c = Class.forName("com.acme.foo.db.impl.UserDaoImpl");
Object obj = c.getConstructor().newInstance();

То есть: отражение - мощное и важное средство, но, если его не остановить, оно может использоваться для проникновения во внутреннее устройство модуля нежелательными способами. У Марка Рейнхольда есть довольно тревожный пример . (Сообщение SO здесь .)

В Jigsaw сильная инкапсуляция дает возможность запретить доступ к классу, включая отражение. (Это может зависеть от настроек командной строки в ожидании пересмотренной технической спецификации для JDK 9.) Обратите внимание, что поскольку Jigsaw используется для самого JDK, Oracle утверждает, что это позволит команде Java быстрее внедрять инновации во внутреннее устройство платформы.

Проблема 3: путь к классам стирает архитектурные связи

У команды обычно есть ментальная модель взаимоотношений между банками. Например, foo-app.jarможет использовать то, foo-services.jarчто использует foo-db.jar. Мы можем утверждать, что классы foo-app.jarне должны обходить «уровень обслуживания» и использовать их foo-db.jarнапрямую. Однако нет способа принудительно применить это через путь к классам. Марк Рейнхольд упоминает об этом здесь .

Для сравнения, Jigsaw предлагает явную и надежную модель доступности для модулей.

Проблема 4: монолитная среда выполнения

Среда выполнения Java является монолитной rt.jar. На моей машине это 60+ МБ с 20к классов! В эпоху микросервисов, устройств IoT и т. Д. Нежелательно иметь на диске Corba, Swing, XML и другие библиотеки, если они не используются.

Jigsaw разбивает сам JDK на множество модулей; например, java.sql содержит знакомые классы SQL. У этого есть несколько преимуществ, но новый jlinkинструмент - это инструмент. Предполагая, что приложение полностью модульное, jlinkгенерирует распространяемый образ времени выполнения, который обрезан, чтобы содержать только указанные модули (и их зависимости). Забегая вперед, Oracle предвидит будущее, в котором модули JDK будут заранее скомпилированы в собственный код. Хотя jlinkэто необязательно, а компиляция AOT является экспериментальной, они являются основными показателями того, куда движется Oracle.

Проблема 5: управление версиями

Хорошо известно, что путь к классам не позволяет нам использовать несколько версий одного и того же jar-файла: например, bar-lib-1.1.jarи bar-lib-2.2.jar.

Jigsaw не решает эту проблему; Марк Рейнхольд излагает здесь обоснование . Суть в том, что Maven, Gradle и другие инструменты представляют собой большую экосистему для управления зависимостями, и другое решение будет скорее вредным, чем полезным.

Следует отметить, что другие решения (например, OSGi) действительно решают эту проблему (и другие, кроме №4).

Нижняя граница

Это некоторые ключевые моменты для Jigsaw, мотивированные конкретными проблемами.

Обратите внимание, что объяснение противоречий между Jigsaw, OSGi, JBoss Modules и т. Д. - это отдельное обсуждение, которое принадлежит другому сайту Stack Exchange. Между решениями гораздо больше различий, чем описано здесь. Более того, был достигнут достаточный консенсус для утверждения бюллетеня для повторного рассмотрения с общественностью для JSR 376.

Майкл Истер
источник
3

В этой статье подробно объясняются проблемы, которые пытаются решить как OSGi, так и JPMS / Jigsaw:

«Java 9, OSGi и будущее модульности» [22 сентября 2016 г.]

В нем также подробно рассматриваются подходы OSGi и JPMS / Jigsaw. На данный момент, похоже, авторы почти не указали практических плюсов для JPMS / Jigsaw по сравнению с зрелой (16-летней) OSGi.

увсмтид
источник