java 在Jar中打开服务声明文件

ehxuflar  于 2023-06-20  发布在  Java
关注(0)|答案(1)|浏览(114)
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

有什么建议吗?还是有别的办法

vcudknz3

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”是它有时根本就不工作,它并不是真的应该工作(规范没有定义它应该工作。它也不完全要求它永远不这样做)。

相关问题