From 58bd057a2460613c87bdb62ecd60e38cc8b13616 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 16 Mar 2024 14:21:34 +0100 Subject: [PATCH 1/2] Avoid cloning empty Annotation array in TypeDescriptor (backport) Closes gh-32405 --- .../core/convert/TypeDescriptor.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 1eb90c823ba..120f676e749 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -52,8 +52,6 @@ import org.springframework.util.ObjectUtils; @SuppressWarnings("serial") public class TypeDescriptor implements Serializable { - private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; - private static final Map, TypeDescriptor> commonTypesCache = new HashMap<>(32); private static final Class[] CACHED_COMMON_TYPES = { @@ -84,7 +82,7 @@ public class TypeDescriptor implements Serializable { public TypeDescriptor(MethodParameter methodParameter) { this.resolvableType = ResolvableType.forMethodParameter(methodParameter); this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType()); - this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ? + this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ? methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations()); } @@ -96,7 +94,7 @@ public class TypeDescriptor implements Serializable { public TypeDescriptor(Field field) { this.resolvableType = ResolvableType.forField(field); this.type = this.resolvableType.resolve(field.getType()); - this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations()); + this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations()); } /** @@ -109,7 +107,7 @@ public class TypeDescriptor implements Serializable { Assert.notNull(property, "Property must not be null"); this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter()); this.type = this.resolvableType.resolve(property.getType()); - this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations()); + this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations()); } /** @@ -125,7 +123,7 @@ public class TypeDescriptor implements Serializable { public TypeDescriptor(ResolvableType resolvableType, @Nullable Class type, @Nullable Annotation[] annotations) { this.resolvableType = resolvableType; this.type = (type != null ? type : resolvableType.toClass()); - this.annotatedElement = new AnnotatedElementAdapter(annotations); + this.annotatedElement = AnnotatedElementAdapter.from(annotations); } @@ -547,12 +545,16 @@ public class TypeDescriptor implements Serializable { public String toString() { StringBuilder builder = new StringBuilder(); for (Annotation ann : getAnnotations()) { - builder.append('@').append(ann.annotationType().getName()).append(' '); + builder.append('@').append(getName(ann.annotationType())).append(' '); } builder.append(getResolvableType()); return builder.toString(); } + private static String getName(Class clazz) { + String canonicalName = clazz.getCanonicalName(); + return (canonicalName != null ? canonicalName : clazz.getName()); + } /** * Create a new type descriptor for an object. @@ -742,15 +744,23 @@ public class TypeDescriptor implements Serializable { * @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class) * @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class) */ - private class AnnotatedElementAdapter implements AnnotatedElement, Serializable { + private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable { + + private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]); - @Nullable private final Annotation[] annotations; - public AnnotatedElementAdapter(@Nullable Annotation[] annotations) { + private AnnotatedElementAdapter(Annotation[] annotations) { this.annotations = annotations; } + private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) { + if (annotations == null || annotations.length == 0) { + return EMPTY; + } + return new AnnotatedElementAdapter(annotations); + } + @Override public boolean isAnnotationPresent(Class annotationClass) { for (Annotation annotation : getAnnotations()) { @@ -775,7 +785,7 @@ public class TypeDescriptor implements Serializable { @Override public Annotation[] getAnnotations() { - return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY); + return (isEmpty() ? this.annotations : this.annotations.clone()); } @Override @@ -784,7 +794,7 @@ public class TypeDescriptor implements Serializable { } public boolean isEmpty() { - return ObjectUtils.isEmpty(this.annotations); + return (this.annotations.length == 0); } @Override @@ -800,7 +810,7 @@ public class TypeDescriptor implements Serializable { @Override public String toString() { - return TypeDescriptor.this.toString(); + return "AnnotatedElementAdapter annotations=" + Arrays.toString(this.annotations); } } From eb8b7c43313c55727dcd6e503475881cc5f1d669 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 16 Mar 2024 14:22:17 +0100 Subject: [PATCH 2/2] Remove superfluous @NonNull declarations --- .../core/metrics/jfr/FlightRecorderStartupStep.java | 6 +++--- .../springframework/mock/web/MockHttpServletRequest.java | 5 ++--- .../test/context/jdbc/SqlScriptsTestExecutionListener.java | 4 +--- .../org/springframework/http/DefaultHttpStatusCode.java | 7 +++---- .../web/testfixture/servlet/MockHttpServletRequest.java | 5 ++--- .../web/servlet/function/DefaultServerRequest.java | 5 ++--- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/metrics/jfr/FlightRecorderStartupStep.java b/spring-core/src/main/java/org/springframework/core/metrics/jfr/FlightRecorderStartupStep.java index 915a52833c9..c3d56b757d1 100644 --- a/spring-core/src/main/java/org/springframework/core/metrics/jfr/FlightRecorderStartupStep.java +++ b/spring-core/src/main/java/org/springframework/core/metrics/jfr/FlightRecorderStartupStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 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. @@ -21,10 +21,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; import org.springframework.core.metrics.StartupStep; -import org.springframework.lang.NonNull; /** * {@link StartupStep} implementation for the Java Flight Recorder. + * *

This variant delegates to a {@link FlightRecorderStartupEvent JFR event extension} * to collect and record data in Java Flight Recorder. * @@ -114,12 +114,12 @@ class FlightRecorderStartupStep implements StartupStep { add(key, value.get()); } - @NonNull @Override public Iterator iterator() { return new TagsIterator(); } + private class TagsIterator implements Iterator { private int idx = 0; diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index f94b21cafcd..3486f5acd03 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -63,7 +63,6 @@ import jakarta.servlet.http.Part; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -1020,7 +1019,7 @@ public class MockHttpServletRequest implements HttpServletRequest { } } - private static String encodeCookies(@NonNull Cookie... cookies) { + private static String encodeCookies(Cookie... cookies) { return Arrays.stream(cookies) .map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue())) .collect(Collectors.joining("; ")); diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java index bda23d8fb45..f219bea1f74 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -35,7 +35,6 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestContextAnnotationUtils; @@ -363,7 +362,6 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen } } - @NonNull private ResourceDatabasePopulator createDatabasePopulator(MergedSqlConfig mergedSqlConfig) { ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding()); diff --git a/spring-web/src/main/java/org/springframework/http/DefaultHttpStatusCode.java b/spring-web/src/main/java/org/springframework/http/DefaultHttpStatusCode.java index cc2f754e8e5..0cf6650939d 100644 --- a/spring-web/src/main/java/org/springframework/http/DefaultHttpStatusCode.java +++ b/spring-web/src/main/java/org/springframework/http/DefaultHttpStatusCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -18,7 +18,6 @@ package org.springframework.http; import java.io.Serializable; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; /** @@ -80,8 +79,8 @@ final class DefaultHttpStatusCode implements HttpStatusCode, Comparable c.getName() + '=' + (c.getValue() == null ? "" : c.getValue())) .collect(Collectors.joining("; ")); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java index c70f5848843..ed44e6b057b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java @@ -60,7 +60,6 @@ import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.RequestPath; import org.springframework.http.server.ServletServerHttpRequest; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -522,7 +521,7 @@ class DefaultServerRequest implements ServerRequest { } @Override - public boolean addAll(@NonNull Collection> c) { + public boolean addAll(Collection> c) { throw new UnsupportedOperationException(); } @@ -537,7 +536,7 @@ class DefaultServerRequest implements ServerRequest { } @Override - public boolean retainAll(@NonNull Collection c) { + public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); }