В 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». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.