Как заставить рефлексию работать в JDK 16 и новее?
У меня есть следующий устаревший код, который я перенес на Java 16, но он не работает:
try {
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(URLClassLoader.getSystemClassLoader(), file.toURI().toURL());
} catch (Exception e) {
e.printStackTrace();
}
... Есть способ заставить его работать?
5 ответов
Наконец-то я нашел следующие способы решения этой проблемы:
- добавляя
--add-opens=java.base/java.net=ALL-UNNAMED
параметр исполняемого файла JVM (в случае безымянного модуля) - добавляя
--add-opens=java.base/java.net=MY-MODULE-NAME
параметр исполняемого файла JVM (в случае именованного модуля) - с ядром Burningwave :
- вызывая метод
org.burningwave.core.assembler.StaticComponentContainer.Modules.exportAllToAll()
- или вызвав метод
addClassPath
изClassLoaders
составная часть:
- вызывая метод
org.burningwave.core.assembler.StaticComponentContainer.ClassLoaders.addClassPath(
new
URLClassLoader(
new URL[] {}
),
new File("C:/external-folder/my.jar").toURI().toURL()
)
- или вызвав метод
invoke
изMethods
составная часть:
org.burningwave.core.assembler.StaticComponentContainer.Methods.invoke(
new URLClassLoader(new URL[] {}),
"addURL",
new File("C:/external-folder/my.jar").toURI().toURL()
);
Проблема не в том, что отражение «не работает»; дело в том, что отражение, наконец, обеспечивает большую часть модели доступности, которую всегда применяли компилятор и среда выполнения.
URLClassLoader::addUrl
предназначен только для подклассов; он не предназначен для доступа извне реализации, что вы делаете. Со временем, начиная с Java 9 и продолжая в более поздних версиях (включая 17), ограничения доступа все чаще распознаются путем отражения с предупреждениями, чтобы дать сломанному коду возможность перейти на что-то поддерживаемое.
Рассматриваемый код действительно работал случайно; это зависело от возможности взломать неподдерживаемый интерфейс. С использованием
setAccessible
должно быть подсказкой. Конечно, вы можете попасть в запертые дома, разбив окна, но если вам нужно разбить окно (а это не ваш дом), вы должны знать, в чем проблема.
Посмотрите на него как на наполовину полный; этот случайно сработавший код работал долгое время. Но счет пришел к оплате; время исправить ваш код.
Код очень странный. На первый взгляд, я подумал, что он использует отражение для доступа к внутренним компонентам, чтобы добавить
myJar
в существующий загрузчик классов , но вместо этого он использует его для добавления в новый загрузчик классов . Нет причин для этого. Вы можете просто использоватьURLClassLoader
конструктор ) для этого.
Это должно выглядеть примерно так (не проверено, потому что я не использую IDE):
URL jar = new File("C:/external-folder/my.jar").toURI().toURL();
URL[] urls = { jar };
new URLClassLoader(urls);
Я написал библиотеку Narcissus, которая реализует небольшой API отражения, который полностью обходит все проверки видимости / доступа, средства защиты диспетчера безопасности и строгие меры инкапсуляции в JDK 16+ (на самом деле JDK 7+).
Я слежу за обсуждением с начала этого поста (включая пост о мета stackoverflow) и надеюсь, что меня не убьют :-)
Однако есть два решения для решения этой проблемы без внесения серьезных изменений в код:
- разрешить открытие пакета для всех безымянных модулей через аргумент JVM
-add-opens=java.base/java.net=ALL-UNNAMED
- экспортировать
java.net
пакет для всех безымянных модулей во время выполнения через burnwave:Modules.exportPackageToAllUnnamed ("java.base", "java.net")
Я знаю, что некоторым людям не нравится второе решение, но на данный момент это единственные решения, позволяющие оставить код без изменений.