Spring Boot胖JAR中读取嵌套JAR内配置文件问题
解决Spring Boot胖JAR中读取嵌套JAR内配置文件的问题
Spring官方解决方案
1. 使用Spring Boot Archive API(精准适配所有运行场景)
Spring Boot提供的Archive API专门用于处理胖JAR的内部结构,不管是通过java -jar运行打包后的JAR,还是用gradlew bootRun/maven命令运行开发模式,都能正确遍历BOOT-INF/lib下的嵌套JAR。
代码示例:
import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.JarFileArchive; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public void searchConfFiles() throws Exception { // 获取当前应用的Archive实例,自动适配打包JAR和开发模式 Archive archive = new JarFileArchive(new File(System.getProperty("java.class.path").split(File.pathSeparator)[0])); // 过滤出BOOT-INF/lib下的所有嵌套JAR for (Archive nestedArchive : archive.getNestedArchives(entry -> entry.getName().startsWith("BOOT-INF/lib/"))) { if (nestedArchive instanceof JarFileArchive jarArchive) { try (JarFile jarFile = jarArchive.getJarFile()) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); // 筛选出.conf后缀的非目录文件 if (!entry.isDirectory() && entry.getName().endsWith(".conf")) { try (InputStream inputStream = jarFile.getInputStream(entry)) { String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); System.out.println("Found .conf file: " + URI.create("jar:" + jarArchive.getUrl() + "!/" + entry.getName())); System.out.println(content); } } } } } } }
2. 调整PathMatchingResourcePatternResolver的资源匹配规则
你原来的代码存在两个问题:
classpath:/只能访问当前类路径根目录,无法穿透嵌套JAR;BOOT-INF/lib路径仅在打包后的胖JAR中存在,开发模式下类路径无此结构。
改用classpath*:前缀搭配通用路径,即可扫描所有类路径(包括嵌套JAR)中的.conf文件:
import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import java.io.InputStream; import java.nio.charset.StandardCharsets; public void searchConfFiles() throws Exception { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 扫描所有类路径(含嵌套JAR)下的.conf文件 Resource[] resources = resolver.getResources("classpath*:**/*.conf"); System.out.println("resource length: " + resources.length); for (Resource resource : resources) { if (resource.exists()) { try (InputStream inputStream = resource.getInputStream()) { String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); System.out.println("Found .conf file: " + resource.getURI()); System.out.println(content); } } } }
如果需要仅限定扫描BOOT-INF/lib下的嵌套JAR,Archive API是更精准的选择;若只需收集所有.conf文件,这个方法更简洁。
替代方案(不依赖Spring API)
如果不想依赖Spring Boot特定API,可以用Java原生的JarFile类直接遍历类路径下的所有JAR文件:
import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public void searchConfFiles() throws IOException { String classPath = System.getProperty("java.class.path"); String[] classPathEntries = classPath.split(File.pathSeparator); for (String entryPath : classPathEntries) { File file = new File(entryPath); // 遍历类路径下的所有JAR文件 if (file.isFile() && entryPath.endsWith(".jar")) { try (JarFile jarFile = new JarFile(file)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".conf")) { try (InputStream is = jarFile.getInputStream(jarEntry)) { String content = new String(is.readAllBytes(), StandardCharsets.UTF_8); System.out.println("Found .conf file: " + file.getAbsolutePath() + "!/" + jarEntry.getName()); System.out.println(content); } } } } } } }
这个方法兼容性极强,适用于任何Java应用,无需依赖Spring生态。
内容的提问来源于stack exchange,提问作者Deepu Varma
相关产品推荐
相关产品推荐

