Skip to content

OriginTrackedResource does not implement equals correctly - #52205

Merged
philwebb merged 1 commit into
spring-projects:4.0.xfrom
zhoubiao188:fix-origin-tracked-resource-equals
Oct 17, 2026
Merged

philwebb merged 1 commit into
spring-projects:4.0.xfrom
zhoubiao188:fix-origin-tracked-resource-equals

Conversation

@zhoubiao188

Copy link
Copy Markdown
Contributor

OriginTrackedResource.equals compares the wrapped Resource with the
OriginTrackedResource argument instead of with the argument's wrapped resource:

OriginTrackedResource other = (OriginTrackedResource) obj;
return this.resource.equals(other) && ObjectUtils.nullSafeEquals(this.origin, other.origin);

Resource implementations such as ClassPathResource and FileSystemResource do not
override equals, so this.resource.equals(other) is always false for a distinct
instance. Two wrappers around the same resource and the same origin are therefore never
considered equal:

Resource resource = new ClassPathResource("application.properties");
Origin origin = ...;
OriginTrackedResource a = OriginTrackedResource.of(resource, origin);
OriginTrackedResource b = OriginTrackedResource.of(resource, origin);
a.equals(b);                        // false, expected true
a.hashCode() == b.hashCode();       // true
Set.of(a, b).size();                // 2, expected 1

hashCode (which uses this.resource.hashCode()) already treats the two instances as
equal, so the equals/hashCode contract is broken: such wrappers cannot be
de-duplicated in a set or used as equal map keys.

The existing equalsAndHashCode test only asserted that an instance equals itself
(r1o1a.isEqualTo(r1o1a)) and never compared two distinct wrappers around the same
resource, which is why this went unnoticed. It now asserts that r1o1a is equal to
r1o1b as well. That assertion fails without the fix in this PR and passes with it.

The class already uses ObjectUtils.nullSafeEquals for the origin, so the same helper is
used for the resource for consistency.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 14, 2026
@wilkinsona wilkinsona changed the title Fix OriginTrackedResource equals ignoring the wrapped resource OriginTrackedResource does not implement equals correctly Oct 14, 2026
@wilkinsona wilkinsona added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Oct 14, 2026
@wilkinsona wilkinsona added this to the 4.0.x milestone Oct 14, 2026
Update `OriginTrackedResource` to compare the wrapped resource with
the wrapped resource, rather than the unwrapped with the wrapped.

See spring-projectsgh-52205

Signed-off-by: zhoubiao188 <32508238+zhoubiao188@users.noreply.github.com>
@philwebb
philwebb force-pushed the fix-origin-tracked-resource-equals branch from 8f67af0 to 72a5f8e Compare October 17, 2026 01:54
@philwebb
philwebb merged commit a9060ac into spring-projects:4.0.x Oct 17, 2026
2 checks passed
@philwebb philwebb modified the milestones: 4.0.x, 4.0.9 Oct 17, 2026
@philwebb

Copy link
Copy Markdown
Member

Thanks @zhoubiao188!

opensource-fork-sync Bot pushed a commit to cla7aye15I4nd/opensource-spring-projects__spring-boot that referenced this pull request Oct 17, 2026
The method parameters changed in 4.1 and was missed during the
forward merge.

See spring-projectsgh-52205
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: bug A general bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants