diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java index d0591716924..fbba3a3b2c1 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java @@ -160,14 +160,12 @@ final class SynthesizedMergedAnnotationInvocationHandler i } /** - * This method currently does not address the following issues which we may + * This method currently does not address the following issue which we may * choose to address at a later point in time. * * * @param value the attribute value to format * @return the formatted string representation @@ -199,10 +197,30 @@ final class SynthesizedMergedAnnotationInvocationHandler i return Long.toString((Long) value) + 'L'; } if (type == Float.class) { - return Float.toString((Float) value) + 'f'; + float floatValue = (Float) value; + if (Float.isNaN(floatValue)) { + return "0.0f/0.0f"; + } + if (floatValue == Float.POSITIVE_INFINITY) { + return "1.0f/0.0f"; + } + if (floatValue == Float.NEGATIVE_INFINITY) { + return "-1.0f/0.0f"; + } + return Float.toString(floatValue) + 'f'; } if (type == Double.class) { - return Double.toString((Double) value); + double doubleValue = (Double) value; + if (Double.isNaN(doubleValue)) { + return "0.0/0.0"; + } + if (doubleValue == Double.POSITIVE_INFINITY) { + return "1.0/0.0"; + } + if (doubleValue == Double.NEGATIVE_INFINITY) { + return "-1.0/0.0"; + } + return Double.toString(doubleValue); } if (value instanceof Enum e) { return e.name(); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index 4aa1adff507..29e85299707 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -2087,6 +2087,30 @@ class MergedAnnotationsTests { .isEqualTo("@%s({\"FromValueAttributeMeta\"})", ValueAttribute.class.getCanonicalName()); } + @Test // gh-37244 + void toStringForSynthesizedAnnotationsWithNonFiniteFloatingPointValues() { + Map attributes = Map.of( + "name", "test", + "floatValue", Float.NaN, + "doubleValue", Double.NaN); + RequestMapping mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize(); + assertThat(mapping).asString().contains("floatValue=0.0f/0.0f", "doubleValue=0.0/0.0"); + + attributes = Map.of( + "name", "test", + "floatValue", Float.POSITIVE_INFINITY, + "doubleValue", Double.POSITIVE_INFINITY); + mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize(); + assertThat(mapping).asString().contains("floatValue=1.0f/0.0f", "doubleValue=1.0/0.0"); + + attributes = Map.of( + "name", "test", + "floatValue", Float.NEGATIVE_INFINITY, + "doubleValue", Double.NEGATIVE_INFINITY); + mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize(); + assertThat(mapping).asString().contains("floatValue=-1.0f/0.0f", "doubleValue=-1.0/0.0"); + } + @Test void equalsForSynthesizedAnnotations() throws Exception { Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute"); diff --git a/spring-core/src/test/java/org/springframework/util/backoff/ExponentialBackOffTests.java b/spring-core/src/test/java/org/springframework/util/backoff/ExponentialBackOffTests.java index 39ea225f2ed..01517e0216f 100644 --- a/spring-core/src/test/java/org/springframework/util/backoff/ExponentialBackOffTests.java +++ b/spring-core/src/test/java/org/springframework/util/backoff/ExponentialBackOffTests.java @@ -79,7 +79,17 @@ class ExponentialBackOffTests { } @Test - void maxAttemptsReached() { + void maxIntervalReachedImmediately() { + ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0); + backOff.setMaxInterval(50L); + + BackOffExecution execution = backOff.start(); + assertThat(execution.nextBackOff()).isEqualTo(50L); + assertThat(execution.nextBackOff()).isEqualTo(50L); + } + + @Test + void maxElapsedTimeReached() { ExponentialBackOff backOff = new ExponentialBackOff(2000L, 2.0); backOff.setMaxElapsedTime(4000L); @@ -90,6 +100,19 @@ class ExponentialBackOffTests { assertThat(execution.nextBackOff()).isEqualTo(BackOffExecution.STOP); } + @Test + void maxAttempts() { + ExponentialBackOff backOff = new ExponentialBackOff(); + backOff.setInitialInterval(1000L); + backOff.setMultiplier(2.0); + backOff.setMaxInterval(10000L); + backOff.setMaxAttempts(6); + List delays = new ArrayList<>(); + BackOffExecution execution = backOff.start(); + IntStream.range(0, 7).forEach(i -> delays.add(execution.nextBackOff())); + assertThat(delays).containsExactly(1000L, 2000L, 4000L, 8000L, 10000L, 10000L, BackOffExecution.STOP); + } + @Test void startReturnsDifferentInstances() { ExponentialBackOff backOff = new ExponentialBackOff(); @@ -143,16 +166,6 @@ class ExponentialBackOffTests { assertThat(execution.nextBackOff()).isBetween(4275L, 4724L); } - @Test - void maxIntervalReachedImmediately() { - ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0); - backOff.setMaxInterval(50L); - - BackOffExecution execution = backOff.start(); - assertThat(execution.nextBackOff()).isEqualTo(50L); - assertThat(execution.nextBackOff()).isEqualTo(50L); - } - @Test void toStringContent() { ExponentialBackOff backOff = new ExponentialBackOff(2000L, 2.0); @@ -174,17 +187,4 @@ class ExponentialBackOffTests { assertThat(execution).asString().isEqualTo("ExponentialBackOffExecution[currentInterval=4000ms, multiplier=2.0, attempts=2]"); } - @Test - void maxAttempts() { - ExponentialBackOff backOff = new ExponentialBackOff(); - backOff.setInitialInterval(1000L); - backOff.setMultiplier(2.0); - backOff.setMaxInterval(10000L); - backOff.setMaxAttempts(6); - List delays = new ArrayList<>(); - BackOffExecution execution = backOff.start(); - IntStream.range(0, 7).forEach(i -> delays.add(execution.nextBackOff())); - assertThat(delays).containsExactly(1000L, 2000L, 4000L, 8000L, 10000L, 10000L, BackOffExecution.STOP); - } - }