我可以组装一个很长的 --add-opens 标志列表,但我希望有一种更干净的方法来允许反射所有 JRE 类。我的代码需要使用反射来执行许多操作,例如获取 java.util.logging.FileHandler 的 files[] 值,但是这样的情况很多,我没有时间广泛测试每个可能的操作找到所有可能在运行时中断的方法。
有人可以提供 1) 一个简短的命令行启动参数来启用此功能,或者 2) 一个完整的 --add-opens 命令列表来为每个 JRE 类启用反射?
告诉我不要使用反射的答案是没有帮助的,将被否决。
感谢@Holger 和@Slaw 的提示;这个类需要为它所在的任何模块打开 java.base/java.lang,然后它将为类加载器打开 ALL-UNNAMED 模块的所有模块。
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
final Module unnamedModule = AddAllOpens.class.getClassLoader().getUnnamedModule();
final Method method = Module.class.getDeclaredMethod( "implAddExportsOrOpens", String.class, Module.class, boolean.class, boolean.class );
method.setAccessible( true );
ModuleLayer.boot().modules().forEach( module -> {
try {
final Set<String> packages = module.getPackages();
for( String eachPackage : packages ) {
method.invoke( module, eachPackage, unnamedModule, true, true );
log.info( "--add-open " + module.getName() + "/" + eachPackage + "=" + unnamedModule.toString() );
}
} catch( IllegalAccessException | InvocationTargetException e ) {
throw new RuntimeException( e );
}
} );
}
正如我在评论中指出的,这似乎正是 Java 开发人员试图阻止其他开发人员做的事情。他们越来越多地迫使开发人员在破坏封装时“明确”。因此,我怀疑是否有一种快速简便的方法可以将运行时映像中的所有模块打开以进行反射。 也就是说,您可以使用
jimage
工具列出图像的所有内容。可以轻松解析输出以将包名称与模块名称关联起来。这意味着您可以编写一个脚本来为图像中的每个包构建一个
--add-opens
参数。警告: JVM 似乎将 --add-opens
参数的数量限制为 1,000(一千)
1。 调用
jimage
(命令行)的示例:
jimage list <JAVA_HOME>/lib/modules
示例脚本(Python):
import os
import sys
from subprocess import Popen, PIPE
# Usage: python script.py <jdk_home> [targets]
#
# jdk_home - absolute or relative path to a JDK installation
# targets - optional comma-separated list of modules as targets for --add-opens (default: ALL-UNNAMED)
if __name__ == '__main__':
jdk_home = os.path.abspath(sys.argv[1])
command = os.path.join(jdk_home, 'bin', 'jimage')
image = os.path.join(jdk_home, 'lib', 'modules')
targets = 'ALL-UNNAMED' if len(sys.argv) < 3 else sys.argv[2]
with Popen([command, 'list', image], text=True, stdout=PIPE) as proc:
visited_pkgs = set()
for line in proc.stdout.readlines()[1:]:
line = line.strip()
if len(line) == 0:
continue
elif line.startswith('Module: '):
mod_name = line[len('Module: '):]
elif line.endswith('.class') and line != 'module-info.class':
pkg_name = line[:len(line) - len('.class')]
pkg_name = pkg_name.replace('/', '.')
pkg_name = pkg_name[0:pkg_name.rindex('.')]
if pkg_name not in visited_pkgs:
visited_pkgs.add(pkg_name)
print(f'--add-opens={mod_name}/{pkg_name}={targets}')
--add-opens
参数限制为
java.*
模块中的包会导致 536 个参数。在 jdk.*
模块中包含包会导致 843 个参数。另外,在 javafx.*
模块中包含包(对于那些包含 JavaFX 的 JDK)会导致 1,021 个参数。没有尝试将软件包限制为仅包含其模块 exports
的软件包。
val method = Module::class.java.getDeclaredMethod("implAddOpens", String::class.java)
method.isAccessible = true
ModuleLayer.boot().modules().forEach{ module ->
module.packages.forEach {
method.invoke(module, it)
}
}
与之前的解决方案一样,您还必须向此代码所在的模块打开 java.base/java.lang,但您可以使用 --add-opens 参数来完成此操作。希望这有帮助。