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);
}
我一直在尝试读取项目中的服务声明文件(然后将它们保存为Map,其中Interface名称作为键,Provider名称作为值),包括来自导入Jars的服务声明文件,但在从JARS内部阅读文件时遇到了问题。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
有什么建议吗?还是有别的办法
1条答案
按热度按时间vcudknz31#
classLoader.getResources("META-INF/services");
这行不通。类加载器不“做”目录,也不“做”一个“列表”命令。这就是为什么
META-INF/services
存在的原因!而不是'给予我一个例如的列表。类路径上的所有类文件,这不是类加载器提供的抽象,你确实有操作“给予我资源X的所有变体,即如果该资源存在不止一次,则我需要所有资源”,由SPI(META-INF/services
)使用:编译后的代码包含一个列出类名的已知文件名。这解决了“无法运行列表命令”的困境:不需要列出任何东西,只需获取您感兴趣的接口的服务文件,然后加载该文件中列出的每个类。这种情况通常通过使用SPI来解决。您需要某种奇怪的元SPI,其中需要某个文件存在的所有SPI服务的列表。这根本不是一个东西。你不能随心所欲。
你可以破解它-确定哪些jar和dirs-on-disk是类路径的一部分,并扫描它们。这需要大量的代码,你需要请求一个众所周知的资源(不是一个目录,一个实际的文件),
toString()
你得到的URL,然后把它撕成碎片,知道该怎么做。这并不完全符合规则--类加载器根本不“必须”是基于jar/磁盘的,它可以动态生成资源,从网络查询它们,或者从数据库加载它们。考虑到它是一个可插入的架构,并且插件不(也不能)实现“list”命令,这样的攻击是不完整的。所以,这不是个好主意。
不管你怎么想:“我知道了,我把
META-INF/services
目录下的所有文件都列出来!“--无论你有什么问题,只要你想尝试一下,那就是错误的答案。请注意,
.getResource("META-INF/services")
有时确实有效。但是规范并不能保证这一点,而且,实际上,正如您所发现的,在大多数平台/类路径-源代码组合中,它并不能保证这一点。“bug”是它有时根本就不工作,它并不是真的应该工作(规范没有定义它应该工作。它也不完全要求它永远不这样做)。