From a380ca27508a028f7eb48112760c6fda93c511d2 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 5 Oct 2022 16:26:58 +0200 Subject: [PATCH] Ensure ClassPathResources with same path and ClassLoader are equal Prior to this commit, if two ClassPathResource instances were constructed differently (one from an absolute path and one from a path relative to a Class) but had the same absolute path and the same ClassLoader, they were effectively equal, but ClassPathResource#equals returned false. This commit addresses this by revising the logic in ClassPathResource#equals accordingly. Closes gh-29263 --- .../core/io/ClassPathResource.java | 12 ++++++--- .../core/io/ClassPathResourceTests.java | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java index 5015b644221..d9b6dc1fe48 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -24,7 +24,6 @@ import java.net.URL; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -271,9 +270,14 @@ public class ClassPathResource extends AbstractFileResolvingResource { if (!(other instanceof ClassPathResource otherRes)) { return false; } - return (this.path.equals(otherRes.path) && - ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && - ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz)); + if (!this.absolutePath.equals(otherRes.absolutePath)) { + return false; + } + ClassLoader thisClassLoader = + (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader()); + ClassLoader otherClassLoader = + (otherRes.classLoader != null ? otherRes.classLoader : otherRes.clazz.getClassLoader()); + return thisClassLoader.equals(otherClassLoader); } /** diff --git a/spring-core/src/test/java/org/springframework/core/io/ClassPathResourceTests.java b/spring-core/src/test/java/org/springframework/core/io/ClassPathResourceTests.java index 63fcd6fbf4c..fb1d4df16ce 100644 --- a/spring-core/src/test/java/org/springframework/core/io/ClassPathResourceTests.java +++ b/spring-core/src/test/java/org/springframework/core/io/ClassPathResourceTests.java @@ -30,6 +30,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.springframework.core.OverridingClassLoader; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.io.CleanupMode.NEVER; @@ -79,6 +81,29 @@ class ClassPathResourceTests { assertThat(resource2).isEqualTo(resource1); } + @Test + void resourcesWithEquivalentAbsolutePathsFromTheSameClassLoaderAreEqual() { + Resource resource1 = new ClassPathResource("Resource.class", getClass()); + Resource resource2 = new ClassPathResource("org/springframework/core/io/Resource.class", getClass().getClassLoader()); + assertThat(resource1).isEqualTo(resource2); + assertThat(resource2).isEqualTo(resource1); + } + + @Test + void resourcesWithEquivalentAbsolutePathsFromDifferentClassLoadersAreNotEqual() { + class SimpleThrowawayClassLoader extends OverridingClassLoader { + SimpleThrowawayClassLoader(ClassLoader parent) { + super(parent); + } + } + + Resource resource1 = new ClassPathResource("Resource.class", getClass()); + Resource resource2 = new ClassPathResource("org/springframework/core/io/Resource.class", + new SimpleThrowawayClassLoader(getClass().getClassLoader())); + assertThat(resource1).isNotEqualTo(resource2); + assertThat(resource2).isNotEqualTo(resource1); + } + @Test void relativeResourcesAreEqual() throws Exception { Resource resource = new ClassPathResource("dir/");