提问人:Teddy Tsai 提问时间:6/19/2023 更新时间:6/19/2023 访问量:34
在 Jar 中打开服务声明文件
Opening service declaration file inside a Jar
问:
List<URL> resources = new ArrayList<>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> importedResources = classLoader.getResources("META-INF/services");
while (importedResources.hasMoreElements()) {
resources.add(importedResources.nextElement());
}
Map<String, String> fileContents = Maps.newHashMap();
for (URL resource : resources) {
InputStream stream = classLoader.getResourceAsStream(resource.getFile());
Properties props = new Properties(); // for debug
props.load(stream); // for debug
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream()));
String line;
while ((line = reader.readLine()) != null) { // Line is always null except for the declarations in my own project
String fileName = line;
if (!fileName.endsWith("/")) {
URL fileUrl = new URL(resource + "\\" + fileName);
BufferedReader fileReader = new BufferedReader(new InputStreamReader(fileUrl.openStream()));
StringBuilder content = new StringBuilder();
String fileLine;
while ((fileLine = fileReader.readLine()) != null) {
content.append(fileLine);
}
fileReader.close();
fileContents.put(fileName, content.toString());
}
}
}
System.out.println(fileContents);
} catch (IOException e) {
throw new RuntimeException(e);
}
我一直在尝试读取我的项目中的服务声明文件(然后将它们保存为映射,将接口名称保存为键,将提供程序名称保存为值),包括导入的 Jar 中的文件,但在从 JARS 内部读取文件时遇到了困难。 Rhe URL 资源将正确加载,并且 importedResources 确实包含文件的 URI,但在尝试读取 Line 时,它将返回 null。
项目是 Maven, URL 如下所示:
jar:file:/C:/Users/xxx/.m2/repository/org/glassfish/hk2/hk2-locator/2.5.0-b42/hk2-locator-2.5.0-b42.jar!/META-INF/services
有什么建议吗?还是有其他方法?
答:
classLoader.getResources("META-INF/services");
这是行不通的。类加载器不“执行”目录,也不“执行”“列表”命令。这就是首先存在的原因!而不是“给我一个列表,例如类路径上的所有类文件”,这根本不是类加载器提供的抽象,你确实有操作“给我资源X的所有变体,即如果该资源存在不止一次,我想要它们”,并且由SPI()使用: 编译的代码包含一个列出类名的已知文件名。这解决了“无法运行列表命令”的困境:无需列出任何内容,只需获取您感兴趣的接口的 services 文件,然后加载该文件中列出的每个类。META-INF/services
META-INF/services
这种情况通常通过使用 SPI 来解决。你想要某种奇怪的元SPI,你想要一个存在某些文件的所有SPI服务的列表。这根本不是一回事。你不能做你想做的事。
您可以破解它 - 确定哪些 jar 和 dirs-on-disk 是类路径的一部分并扫描它们。这需要大量的代码,你需要要求一个众所周知的资源(不是目录,一个实际的文件),你得到的URL,然后把它撕成碎片,知道该怎么做。这并不完全符合规则 - 类加载器根本不需要基于 jar/磁盘,它可以动态生成资源,从网络查询它们,或从数据库加载它们。鉴于它是一个可插拔的架构,并且插件没有(也不能)实现“列表”命令,这样的黑客是不完整的。toString()
因此,这不是一个好主意。
不管是什么让你想:“我知道!我只列出目录中的所有文件!“——对于导致您尝试该答案的任何问题,这都是错误的答案。META-INF/services
请注意,这有时确实有效。但是规范并不能保证这一点,事实上,正如你所发现的,在大多数平台/类路径-源组合中,它并不能保证这一点。“错误”是它有时根本可以工作,但实际上不应该这样做(规范没有定义它应该工作。它并不完全要求它从不这样做)。.getResource("META-INF/services")
评论
System.out.println(SomeClass.class.getResource("SomeClass.class"))
,对于任何类,都会打印一个字符串,该字符串通常会告诉您从哪个位置加载类的确切位置。您可以轻松地检查该事物的目录。META-INF/services
评论