From ce87285be5d4be7ab9d981273acac8a8624883dc Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 10 Feb 2022 14:16:34 +0100 Subject: [PATCH] Use canonical names for types in synthesized annotation toString My proposal for the same change in the JDK is currently targeted for JDK 19. - https://bugs.openjdk.java.net/browse/JDK-8281462 - https://bugs.openjdk.java.net/browse/JDK-8281568 - https://github.com/openjdk/jdk/pull/7418 See gh-28015 --- ...izedMergedAnnotationInvocationHandler.java | 9 +++++++-- .../annotation/MergedAnnotationsTests.java | 20 +++++++++++++------ .../test/context/BootstrapUtilsTests.java | 6 +++--- 3 files changed, 24 insertions(+), 11 deletions(-) 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 6838dc3779..8cbfcc6f5a 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 @@ -177,7 +177,7 @@ final class SynthesizedMergedAnnotationInvocationHandler i private String annotationToString() { String string = this.string; if (string == null) { - StringBuilder builder = new StringBuilder("@").append(this.type.getName()).append('('); + StringBuilder builder = new StringBuilder("@").append(getName(this.type)).append('('); for (int i = 0; i < this.attributes.size(); i++) { Method attribute = this.attributes.get(i); if (i > 0) { @@ -202,7 +202,7 @@ final class SynthesizedMergedAnnotationInvocationHandler i return ((Enum) value).name(); } if (value instanceof Class) { - return ((Class) value).getName() + ".class"; + return getName((Class) value) + ".class"; } if (value.getClass().isArray()) { StringBuilder builder = new StringBuilder("{"); @@ -277,6 +277,11 @@ final class SynthesizedMergedAnnotationInvocationHandler i return (A) Proxy.newProxyInstance(classLoader, interfaces, handler); } + private static String getName(Class clazz) { + String canonicalName = clazz.getCanonicalName(); + return (canonicalName != null ? canonicalName : clazz.getName()); + } + private static boolean isVisible(ClassLoader classLoader, Class interfaceClass) { if (classLoader == interfaceClass.getClassLoader()) { 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 0b92547863..a6ff5a0f07 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 @@ -1887,15 +1887,22 @@ class MergedAnnotationsTests { // Formatting common to Spring and JDK 9+ assertThat(string) - .startsWith("@" + RequestMapping.class.getName() + "(") - .contains("value={\"/test\"}", "path={\"/test\"}", "name=\"bar\"", "clazz=java.lang.Object.class") + .contains("value={\"/test\"}", "path={\"/test\"}", "name=\"bar\"") .endsWith(")"); if (webMapping instanceof SynthesizedAnnotation) { - assertThat(string).as("Spring uses Enum#name()").contains("method={GET, POST}"); + assertThat(string).as("Spring formatting") + .startsWith("@org.springframework.core.annotation.MergedAnnotationsTests.RequestMapping(") + .contains("method={GET, POST}", + "clazz=org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod.class", + "classes={org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod.class}"); } else { - assertThat(string).as("JDK uses Enum#toString()").contains("method={method: get, method: post}"); + assertThat(string).as("JDK 9-18 formatting") + .startsWith("@org.springframework.core.annotation.MergedAnnotationsTests$RequestMapping(") + .contains("method={method: get, method: post}", + "clazz=org.springframework.core.annotation.MergedAnnotationsTests$RequestMethod.class", + "classes={org.springframework.core.annotation.MergedAnnotationsTests$RequestMethod.class}"); } } @@ -2989,8 +2996,9 @@ class MergedAnnotationsTests { RequestMethod[] method() default {}; - // clazz is only used for testing annotation toString() implementations - Class clazz() default Object.class; + Class clazz() default RequestMethod.class; + + Class[] classes() default {RequestMethod.class}; } @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java index 7ed75786f8..5c409d8804 100644 --- a/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -72,8 +72,8 @@ class BootstrapUtilsTests { assertThatIllegalStateException().isThrownBy(() -> resolveTestContextBootstrapper(bootstrapContext)) .withMessageContaining("Configuration error: found multiple declarations of @BootstrapWith") - .withMessageContaining(FooBootstrapper.class.getName()) - .withMessageContaining(BarBootstrapper.class.getName()); + .withMessageContaining(FooBootstrapper.class.getCanonicalName()) + .withMessageContaining(BarBootstrapper.class.getCanonicalName()); } @Test