В JDK 17 вошли 14 JEP, один из которых — JEP 403 (Strongly Encapsulate JDK Internals), — строго инкапсулирует все внутренние элементы JDK, за исключением некоторых критических API, таких как sun.misc.Unsafe. Как преемник JEP 396 (Strongly Encapsulate JDK Internals by Default) он еще больше ограничивает доступ к внутренним API и к ним больше нельзя получить доступ с помощью параметра --illegal-access.

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

Для открытия определенных пакетов можно использовать параметр командной строки --add-opens или атрибут Add-Opens в манифесте JAR-файла. Например, открыть доступ к модулю java.util из всех безымянных модулей можно следующим образом:

$ java --add-opens java.base/java.util=ALL-UNNAMED 
  --classpath {classpath} -jar {jarfile}

Внутренние элементы, такие как криптографические ключи, пока еще доступны через рефлекшн. А также все классы, методы и поля из пакетов sun.* и некоторые внутренние API в пакетах com.sun.*, jdk.* и org.* . Список пакетов, которые не открыты по умолчанию можно посмотреть здесь. Также рефлекшн все еще можно использовать для пакетов  sun.misc и sun.reflect, так как они экспортируются модулем jdk.unsupported.

Строгая инкапсуляция внутренних API постепенно развивалась в течение последних лет, с тех пор как в JDK 9 появилась система модулей, обеспечивающая строгую инкапсуляцию: обращаться можно только к public и protected (через подкласс) классам, методам и полям в пакетах, экспортированных определенным модулем.

В JDK 9 некоторые внутренние API заменены публичными, например, java.util.Base64 (класс, состоящий из статических методов для кодирования и декодирования Base64). Поэтому стоит заменить вызовы внутренних API на публичные.

Новые внутренние элементы, добавленные в JDK 9 и позднее, по умолчанию строго инкапсулированы. Однако для внутренних API, появившихся до JDK 9, строгая инкапсуляция в рантайме отсутствует.

В JDK 9 можно по-прежнему получить доступ к внутренним API через  ослабление строгой инкапсуляции с помощью параметра --illegal-access, впервые представленного в JEP 261. Значение по умолчанию --illegal-access=permit открывает весь внутренний код для безымянных модулей с доступом через рефлекшн. Альтернативой --illegal-access=permit является --illegal-access=warn. В этом случае при каждом доступе через рефлекшн выдается предупреждение. Через --illegal-access=debug доступна дополнительная информация с трассировкой стека. Параметр --illegal-access=deny блокирует любой доступ через рефлекшн, кроме случаев, когда к модулю явно открывается доступ с помощью такого параметра как --add-opens.

В JDK 16 (JEP 396) значение по умолчанию для --illegal-access изменилось с permit на deny (другие значения по-прежнему доступны) и параметр --illegal-access объявлен устаревшим (при его использовании выводится предупреждение).

JDK 17 продолжает ограничение доступа к внутренним API. Параметр --illegal-access больше не разрешает доступ к внутренностям, и вместо него для открытия определенных пакетов используется --add-opens.

Попытка использовать --illegal-access в JDK 17, например, со значением permit,  приведет к предупреждению:

$ java --illegal-access=permit {filename}.java

OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=permit; 
  support was removed in 17.0

Ожидается, что в будущем этот параметр будет полностью удален.

Попытка доступа к внутреннему API приведет к следующему исключению:

java.lang.reflect.InaccessibleObjectException: 
  Unable to make field private final {type} accessible:
  module java.base does not "opens {module}" to unnamed module {module}

В статье использовались материалы от Java-сообщества. Nicolai Parlog, developer advocate в Oracle, рассказал об использовании внутренних API JDK. OkHttp (проект Square Open Source) написали, как JEP 403 повлиял на их проект, использующий внутренние API.


Материал подготовлен в рамках курса «Java Developer. Professional». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Комментарии (0)