From 1055f6b21ab0eb01c959cfcab21ebe7b8796b865 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 7 Apr 2026 22:09:27 +0800 Subject: [PATCH] fix: handle nested jar class path fallback Fixes apolloconfig/apollo#5592 --- CHANGES.md | 2 +- .../apollo/core/utils/ClassLoaderUtil.java | 32 +++++++------ .../core/utils/ClassLoaderUtilTest.java | 47 ++++++++++++++++++- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 737a4b00..a3a9554e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ Apollo Java 2.6.0 ------------------ -* +* [Fix Apollo client local cache fallback for Spring Boot 3 executable JARs](https://github.com/apolloconfig/apollo-java/pull/136) ------------------ All issues and pull requests are [here](https://github.com/apolloconfig/apollo-java/milestone/6?closed=1) diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java index 2528e482..af684763 100644 --- a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java +++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java @@ -17,12 +17,10 @@ package com.ctrip.framework.apollo.core.utils; import com.google.common.base.Strings; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.net.URL; -import java.net.URLDecoder; +import java.nio.file.Paths; /** * @author Jason Song(song_s@ctrip.com) @@ -39,20 +37,11 @@ public class ClassLoaderUtil { loader = ClassLoader.getSystemClassLoader(); } + String defaultClassPath = System.getProperty("user.dir"); try { - URL url = loader.getResource(""); - // get class path - if (url != null) { - classPath = url.getPath(); - classPath = URLDecoder.decode(classPath, "utf-8"); - } - - // 如果是jar包内的,则返回当前路径 - if (Strings.isNullOrEmpty(classPath) || classPath.contains(".jar!")) { - classPath = System.getProperty("user.dir"); - } + classPath = resolveClassPath(loader, defaultClassPath); } catch (Throwable ex) { - classPath = System.getProperty("user.dir"); + classPath = defaultClassPath; logger.warn("Failed to locate class path, fallback to user.dir: {}", classPath, ex); } } @@ -65,6 +54,19 @@ public static String getClassPath() { return classPath; } + static String resolveClassPath(ClassLoader classLoader, String defaultClassPath) throws Exception { + URL url = classLoader.getResource(""); + if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) { + return defaultClassPath; + } + + String resolvedClassPath = Paths.get(url.toURI()).toString(); + if (Strings.isNullOrEmpty(resolvedClassPath)) { + return defaultClassPath; + } + return resolvedClassPath; + } + public static boolean isClassPresent(String className) { try { Class.forName(className); diff --git a/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java b/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java index 8635d5c4..06e2fdb8 100644 --- a/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java +++ b/apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java @@ -18,15 +18,42 @@ import static org.junit.Assert.*; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Test; public class ClassLoaderUtilTest { private static boolean shouldFailInInitialization = false; + @Test public void testGetClassLoader() { assertNotNull(ClassLoaderUtil.getLoader()); } + @Test + public void testResolveClassPathWithFileUrl() throws Exception { + Path tempDir = Files.createTempDirectory("apollo class path"); + try { + ClassLoader classLoader = classLoaderReturning(tempDir.toUri().toURL()); + + assertEquals(tempDir.toString(), ClassLoaderUtil.resolveClassPath(classLoader, "fallback")); + } finally { + Files.deleteIfExists(tempDir); + } + } + + @Test + public void testResolveClassPathFallsBackForNestedJarUrl() throws Exception { + String fallback = "/tmp/fallback"; + URL nestedJarUrl = createUrl("jar:nested:/tmp/apollo-app.jar/!BOOT-INF/classes/!/"); + ClassLoader classLoader = classLoaderReturning(nestedJarUrl); + + assertEquals(fallback, ClassLoaderUtil.resolveClassPath(classLoader, fallback)); + } + @Test public void testIsClassPresent() { assertTrue(ClassLoaderUtil.isClassPresent("java.lang.String")); @@ -50,4 +77,22 @@ public static class ClassWithInitializationError { } } } -} \ No newline at end of file + + private ClassLoader classLoaderReturning(URL resource) { + return new ClassLoader(null) { + @Override + public URL getResource(String name) { + return resource; + } + }; + } + + private URL createUrl(String spec) throws Exception { + return new URL(null, spec, new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL url) { + throw new UnsupportedOperationException(); + } + }); + } +}