openjdk.ruOpenJDK на русском

JEP 260: Encapsulate Most Internal APIs

Инкапсуляция большинства внутренних API

АвторMark Reinhold
ОтветственныйChris Hegarty
ТипFeature
ОбластьJDK
СтатусClosed / Delivered
Выпуск9
Обсуждениеjigsaw dash dev at openjdk dot java dot net
ТрудоёмкостьM
ДлительностьL
БлокируетJEP 261: Module System
Связан сJEP 396: Strongly Encapsulate JDK Internals by Default
JEP 403: Strongly Encapsulate JDK Internals
РецензентыAlan Bateman, Alex Buckley, Brian Goetz, John Rose, Paul Sandoz
ОдобренBrian Goetz
Создан2015/08/03 18:29
Обновлён2024/04/28 18:11
Задача8132928

Аннотация

По умолчанию инкапсулировать большинство внутренних API JDK, чтобы они были недоступны во время компиляции, и подготовиться к будущему выпуску, в котором они будут недоступны и во время выполнения. Обеспечить, чтобы критически важные и широко используемые внутренние API не инкапсулировались и оставались доступными, пока для всей или большей части их функциональности не появятся поддерживаемые замены.

Что не является целью

Этот JEP не определяет замены ни для каких внутренних API: этим занимаются или будут заниматься отдельные JEP и, где это уместно, JSR.

Этот JEP не обязуется сохранять совместимость каких-либо внутренних API между выпусками: они по-прежнему остаются нестабильными и могут меняться без предупреждения.

Мотивация

Некоторые популярные библиотеки используют нестандартные, нестабильные и неподдерживаемые API, которые являются внутренними деталями реализации JDK и никогда не предназначались для внешнего использования. В модульном JDK (JEP 200) ограничение доступа к этим API средствами модульной системы (JEP 261) повышает целостность и безопасность платформы, поскольку многие из этих внутренних API определяют привилегированные операции, важные для безопасности. В долгосрочной перспективе это изменение снизит затраты тех, кто сопровождает сам JDK, а также тех, кто сопровождает библиотеки и приложения, которые, сознательно или нет, используют эти внутренние API.

Описание

На основе анализа различных больших массивов кода, включая Maven Central, а также отзывов, полученных после выхода JDK 8 и его инструмента анализа зависимостей (jdeps), мы делим внутренние API JDK на две большие категории:

  • Некритичные внутренние API, которые, по-видимому, не используются кодом за пределами JDK или используются внешним кодом лишь для удобства, т. е. ради функциональности, которая есть в поддерживаемых API или легко может быть предоставлена библиотеками (например, sun.misc.BASE64Decoder); и

  • Критически важные внутренние API (например, sun.misc.Unsafe), которые предоставляют критически важную функциональность, которую было бы трудно, если не невозможно, реализовать вне самого JDK.

Критически важные внутренние API в JDK 9 инкапсулируются или нет в зависимости от того, существуют ли для них поддерживаемые замены в JDK 8. Поддерживаемая замена — это API, который либо входил в стандарт Java SE 8, т. е. находился в пакете java.* или javax.*, либо был специфичен для JDK и помечен аннотацией @jdk.Exported, обычно в пакете com.sun.* или jdk.*. Подробнее:

  • Критически важные внутренние API, для которых в JDK 8 существуют поддерживаемые замены, в JDK 9 инкапсулированы.

  • Критически важные внутренние API, для которых в JDK 8 не было поддерживаемых замен, в JDK 9 не инкапсулированы. Подробный список приведён ниже.

  • Критически важные внутренние API, для которых в JDK 9 существуют поддерживаемые замены, помечены как Deprecated (устаревший) и в будущем выпуске будут либо инкапсулированы, либо удалены.

Все некритичные внутренние API в JDK 9 инкапсулированы.

Внутренние API, инкапсулированные в JDK 9, недоступны во время компиляции. Их можно сделать доступными во время компиляции с помощью параметра командной строки --add-exports. Во время выполнения они остаются доступными, если были доступны в JDK 8, но в будущем выпуске станут недоступными, и тогда сделать их доступными и во время выполнения можно будет параметрами --add-exports или --add-opens. Параметр --illegal-access управляет доступностью этих API во время выполнения и может использоваться, чтобы имитировать будущую недоступность внутренних API во время выполнения.

Критически важные внутренние API, не инкапсулированные в JDK 9

Здесь перечислены критически важные внутренние API, которые не инкапсулированы в JDK 9, потому что в JDK 8 для них не было поддерживаемых замен.

  • sun.misc.{Signal,SignalHandler}

  • sun.misc.Unsafe (Функциональность многих методов этого класса доступна через variable handles (JEP 193).)

  • sun.reflect.Reflection::getCallerClass(int) (Функциональность этого метода доступна в API обхода стека, определённом в JEP 259.)

  • sun.reflect.ReflectionFactory

  • com.sun.nio.file.{ExtendedCopyOption,ExtendedOpenOption, ExtendedWatchEventModifier,SensitivityWatchEventModifier}

Эти API определены и экспортируются специфичным для JDK модулем jdk.unsupported. Этот модуль присутствует в полных образах JRE и JDK. Поэтому эти API по умолчанию доступны коду в class path и доступны коду в модулях, если эти модули объявляют зависимость от модуля jdk.unsupported.

Критически важные внутренние API, для которых в JDK 9 появились замены, в JDK 9 помечены как Deprecated и в будущем выпуске будут либо инкапсулированы, либо удалены.

Поскольку jdk.unsupported экспортирует и открывает пакеты sun.misc и sun.reflect, все некритичные внутренние API в этих пакетах были, в зависимости от ситуации, либо перенесены в другой пакет, либо удалены. Стандартные модули и модули JDK, которые не подлежат обновлению, не должны зависеть от модуля jdk.unsupported, а должны использовать соответствующие внутренние API.

Те, кто сопровождает библиотеки, использующие критически важные внутренние API, для которых в JDK 9 есть замены, могут воспользоваться Multi-Release JAR Files (JEP 238), чтобы поставлять единый артефакт, который в выпусках до JDK 9 использует старые API, а в более поздних выпусках — API-замены.

Риски и допущения

Если какой-либо широко используемый критически важный внутренний API не был признан критически важным и был перенесён или удалён, то приложения, зависящие от него, перестанут работать.

Если какой-либо широко используемый критически важный внутренний API не был признан критически важным, но всё ещё существует, то приложения, зависящие от него, могут вызывать предупреждение в этом выпуске и перестанут работать в будущем выпуске.

Краткосрочное решение в обоих случаях — конечный пользователь открывает доступ к API с помощью упомянутого выше параметра командной строки; в более долгосрочной перспективе, в одном из следующих выпусков, API может быть перенесён в модуль jdk.unsupported и экспортирован для внешнего использования.

Некритичные внутренние API, ранее находившиеся в пакетах sun.misc и sun.reflect, были либо перенесены, либо удалены. Существующий код, который от них зависит, может работать некорректно.

Зависимости

JEP 200 (The Modular JDK) определяет модульную структуру JDK, а JEP 261 (Module System) реализует модульную систему.