diff --git a/spring-web/src/main/java/org/springframework/http/ProblemDetail.java b/spring-web/src/main/java/org/springframework/http/ProblemDetail.java index 3044b0642d45..fb5dfe6d5aa2 100644 --- a/spring-web/src/main/java/org/springframework/http/ProblemDetail.java +++ b/spring-web/src/main/java/org/springframework/http/ProblemDetail.java @@ -38,11 +38,11 @@ * additional properties. Subclasses can use the protected copy constructor to * re-create an existing {@code ProblemDetail} instance as the subclass, e.g. * from an {@code @ControllerAdvice} such as - * {@link org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler}. + * {@link org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler} or + * {@link org.springframework.web.reactive.result.method.annotation.ResponseEntityExceptionHandler}. * * @author Rossen Stoyanchev * @author Juergen Hoeller - * @author Yanming Zhou * @since 6.0 * @see RFC 7807 * @see org.springframework.web.ErrorResponse @@ -239,8 +239,8 @@ public boolean equals(@Nullable Object other) { if (!(other instanceof ProblemDetail otherDetail)) { return false; } - return (this.type.equals(otherDetail.type) && - ObjectUtils.nullSafeEquals(this.getTitle(), otherDetail.getTitle()) && + return (getType().equals(otherDetail.getType()) && + ObjectUtils.nullSafeEquals(getTitle(), otherDetail.getTitle()) && this.status == otherDetail.status && ObjectUtils.nullSafeEquals(this.detail, otherDetail.detail) && ObjectUtils.nullSafeEquals(this.instance, otherDetail.instance) && @@ -250,7 +250,7 @@ public boolean equals(@Nullable Object other) { @Override public int hashCode() { int result = this.type.hashCode(); - result = 31 * result + ObjectUtils.nullSafeHashCode(this.getTitle()); + result = 31 * result + ObjectUtils.nullSafeHashCode(getTitle()); result = 31 * result + this.status; result = 31 * result + ObjectUtils.nullSafeHashCode(this.detail); result = 31 * result + ObjectUtils.nullSafeHashCode(this.instance); diff --git a/spring-web/src/main/java/org/springframework/web/ErrorResponse.java b/spring-web/src/main/java/org/springframework/web/ErrorResponse.java index adbbfc42b847..2ae0187d705c 100644 --- a/spring-web/src/main/java/org/springframework/web/ErrorResponse.java +++ b/spring-web/src/main/java/org/springframework/web/ErrorResponse.java @@ -102,8 +102,7 @@ default Object[] getDetailMessageArguments(MessageSource messageSource, Locale l /** * Return a code to use to resolve the problem "title" for this exception * through a {@link MessageSource}. - *

By default this is initialized via - * {@link #getDefaultTitleMessageCode(Class, String)}. + *

By default this is initialized via {@link #getDefaultTitleMessageCode(Class)}. */ default String getTitleMessageCode() { return getDefaultTitleMessageCode(getClass()); diff --git a/spring-web/src/test/java/org/springframework/http/ProblemDetailTests.java b/spring-web/src/test/java/org/springframework/http/ProblemDetailTests.java index 9785fd525945..fa3ea5d02033 100644 --- a/spring-web/src/test/java/org/springframework/http/ProblemDetailTests.java +++ b/spring-web/src/test/java/org/springframework/http/ProblemDetailTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ class ProblemDetailTests { @Test - void equalsAndHashCode() throws Exception { + void equalsAndHashCode() { ProblemDetail pd1 = ProblemDetail.forStatus(500); ProblemDetail pd2 = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); ProblemDetail pd3 = ProblemDetail.forStatus(HttpStatus.NOT_FOUND); @@ -50,12 +50,19 @@ void equalsAndHashCode() throws Exception { assertThat(pd2).isNotEqualTo(pd4); assertThat(pd1.hashCode()).isNotEqualTo(pd3.hashCode()); assertThat(pd1.hashCode()).isNotEqualTo(pd4.hashCode()); + } + + @Test // gh-30294 + void equalsAndHashCodeWithDeserialization() throws Exception { + ProblemDetail originalDetail = ProblemDetail.forStatus(500); + + ObjectMapper mapper = new ObjectMapper(); + byte[] bytes = mapper.writeValueAsBytes(originalDetail); + ProblemDetail deserializedDetail = mapper.readValue(bytes, ProblemDetail.class); - ObjectMapper om = new ObjectMapper(); - ProblemDetail pd5 = om.readValue(om.writeValueAsBytes(pd1), ProblemDetail.class); - assertThat(pd1).isEqualTo(pd5); - assertThat(pd5).isEqualTo(pd1); - assertThat(pd1.hashCode()).isEqualTo(pd5.hashCode()); + assertThat(originalDetail).isEqualTo(deserializedDetail); + assertThat(deserializedDetail).isEqualTo(originalDetail); + assertThat(originalDetail.hashCode()).isEqualTo(deserializedDetail.hashCode()); } }