Browse Source

Specify generic type nullness in spring-core

Also in spring-core-test.

See gh-34140
pull/34266/head
Sébastien Deleuze 11 months ago
parent
commit
9d9383aaeb
  1. 6
      spring-core-test/src/main/java/org/springframework/core/test/tools/CompileWithForkedClassLoaderClassLoader.java
  2. 4
      spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java
  3. 4
      spring-core/src/main/java/org/springframework/aot/generate/MethodReference.java
  4. 6
      spring-core/src/main/java/org/springframework/core/MethodParameter.java
  5. 15
      spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java
  6. 6
      spring-core/src/main/java/org/springframework/core/ResolvableType.java
  7. 4
      spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java
  8. 6
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  9. 14
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java
  10. 6
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java
  11. 6
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  12. 4
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationsProcessor.java
  13. 32
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java
  14. 4
      spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java
  15. 16
      spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationCollectors.java
  16. 11
      spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java
  17. 17
      spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotations.java
  18. 6
      spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java
  19. 12
      spring-core/src/main/java/org/springframework/core/type/AnnotatedTypeMetadata.java
  20. 7
      spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java
  21. 7
      spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java
  22. 6
      spring-core/src/main/java/org/springframework/util/CollectionUtils.java
  23. 10
      spring-core/src/main/java/org/springframework/util/CompositeCollection.java
  24. 12
      spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java
  25. 12
      spring-core/src/main/java/org/springframework/util/MethodInvoker.java
  26. 4
      spring-core/src/main/java/org/springframework/util/MultiValueMap.java
  27. 10
      spring-core/src/main/java/org/springframework/util/ObjectUtils.java
  28. 6
      spring-core/src/main/java/org/springframework/util/StringUtils.java
  29. 21
      spring-core/src/main/java/org/springframework/util/function/SingletonSupplier.java
  30. 5
      spring-core/src/main/java/org/springframework/util/function/SupplierUtils.java
  31. 15
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

6
spring-core-test/src/main/java/org/springframework/core/test/tools/CompileWithForkedClassLoaderClassLoader.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -37,7 +37,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader { @@ -37,7 +37,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader {
private final ClassLoader testClassLoader;
private Function<String, byte[]> classResourceLookup = name -> null;
private Function<String, byte @Nullable []> classResourceLookup = name -> null;
public CompileWithForkedClassLoaderClassLoader(ClassLoader testClassLoader) {
@ -48,7 +48,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader { @@ -48,7 +48,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader {
// Invoked reflectively by DynamicClassLoader
@SuppressWarnings("unused")
void setClassResourceLookup(Function<String, byte[]> classResourceLookup) {
void setClassResourceLookup(Function<String, byte @Nullable []> classResourceLookup) {
this.classResourceLookup = classResourceLookup;
}

4
spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -71,7 +71,7 @@ public class DynamicClassLoader extends ClassLoader { @@ -71,7 +71,7 @@ public class DynamicClassLoader extends ClassLoader {
"setClassResourceLookup", Function.class);
ReflectionUtils.makeAccessible(setClassResourceLookupMethod);
ReflectionUtils.invokeMethod(setClassResourceLookupMethod,
getParent(), (Function<String, byte[]>) this::findClassBytes);
getParent(), (Function<String, byte @Nullable []>) this::findClassBytes);
this.defineClassMethod = lookupMethod(parentClass,
"defineDynamicClass", String.class, byte[].class, int.class, int.class);
ReflectionUtils.makeAccessible(this.defineClassMethod);

4
spring-core/src/main/java/org/springframework/aot/generate/MethodReference.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
@ -106,7 +106,7 @@ public interface MethodReference { @@ -106,7 +106,7 @@ public interface MethodReference {
* @param function the resolver function
* @return a new {@link ArgumentCodeGenerator} instance backed by the function
*/
static ArgumentCodeGenerator from(Function<TypeName, CodeBlock> function) {
static ArgumentCodeGenerator from(Function<TypeName, @Nullable CodeBlock> function) {
return function::apply;
}

6
spring-core/src/main/java/org/springframework/core/MethodParameter.java

@ -706,7 +706,7 @@ public class MethodParameter { @@ -706,7 +706,7 @@ public class MethodParameter {
}
ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
if (discoverer != null) {
String[] parameterNames = null;
@Nullable String[] parameterNames = null;
if (this.executable instanceof Method method) {
parameterNames = discoverer.getParameterNames(method);
}
@ -865,7 +865,7 @@ public class MethodParameter { @@ -865,7 +865,7 @@ public class MethodParameter {
* @return the corresponding MethodParameter instance
* @since 6.1
*/
public static MethodParameter forFieldAwareConstructor(Constructor<?> ctor, int parameterIndex, String fieldName) {
public static MethodParameter forFieldAwareConstructor(Constructor<?> ctor, int parameterIndex, @Nullable String fieldName) {
return new FieldAwareConstructorParameter(ctor, parameterIndex, fieldName);
}
@ -877,7 +877,7 @@ public class MethodParameter { @@ -877,7 +877,7 @@ public class MethodParameter {
private volatile Annotation @Nullable [] combinedAnnotations;
public FieldAwareConstructorParameter(Constructor<?> constructor, int parameterIndex, String fieldName) {
public FieldAwareConstructorParameter(Constructor<?> constructor, int parameterIndex, @Nullable String fieldName) {
super(constructor, parameterIndex);
this.parameterName = fieldName;
}

15
spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -19,12 +19,15 @@ package org.springframework.core; @@ -19,12 +19,15 @@ package org.springframework.core;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow;
import java.util.function.Function;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.FlowAdapters;
import org.reactivestreams.Publisher;
@ -380,13 +383,13 @@ public class ReactiveAdapterRegistry { @@ -380,13 +383,13 @@ public class ReactiveAdapterRegistry {
io.smallrye.mutiny.groups.MultiCreate.class, "publisher", Flow.Publisher.class);
registry.registerReactiveType(uniDesc,
uni -> FlowAdapters.toPublisher((Flow.Publisher<Object>)
ReflectionUtils.invokeMethod(uniToPublisher, ((io.smallrye.mutiny.Uni<?>) uni).convert())),
publisher -> ReflectionUtils.invokeMethod(uniPublisher, io.smallrye.mutiny.Uni.createFrom(),
FlowAdapters.toFlowPublisher(publisher)));
Objects.requireNonNull(ReflectionUtils.invokeMethod(uniToPublisher, ((Uni<?>) uni).convert()))),
publisher -> Objects.requireNonNull(ReflectionUtils.invokeMethod(uniPublisher, Uni.createFrom(),
FlowAdapters.toFlowPublisher(publisher))));
registry.registerReactiveType(multiDesc,
multi -> FlowAdapters.toPublisher((Flow.Publisher<Object>) multi),
publisher -> ReflectionUtils.invokeMethod(multiPublisher, io.smallrye.mutiny.Multi.createFrom(),
FlowAdapters.toFlowPublisher(publisher)));
publisher -> Objects.requireNonNull(ReflectionUtils.invokeMethod(multiPublisher, Multi.createFrom(),
FlowAdapters.toFlowPublisher(publisher))));
}
else {
// Mutiny 1 based on Reactive Streams

6
spring-core/src/main/java/org/springframework/core/ResolvableType.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -825,9 +825,9 @@ public class ResolvableType implements Serializable { @@ -825,9 +825,9 @@ public class ResolvableType implements Serializable {
* @see #getGenerics()
* @see #resolve()
*/
public Class<?>[] resolveGenerics() {
public @Nullable Class<?>[] resolveGenerics() {
ResolvableType[] generics = getGenerics();
Class<?>[] resolvedGenerics = new Class<?>[generics.length];
@Nullable Class<?>[] resolvedGenerics = new Class<?>[generics.length];
for (int i = 0; i < generics.length; i++) {
resolvedGenerics[i] = generics[i].resolve();
}

4
spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -205,7 +205,7 @@ final class SerializableTypeWrapper { @@ -205,7 +205,7 @@ final class SerializableTypeWrapper {
if (returnValue == null) {
return null;
}
Type[] result = new Type[((Type[]) returnValue).length];
@Nullable Type[] result = new Type[((Type[]) returnValue).length];
for (int i = 0; i < result.length; i++) {
result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
}

6
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
@ -478,7 +478,7 @@ public abstract class AnnotatedElementUtils { @@ -478,7 +478,7 @@ public abstract class AnnotatedElementUtils {
* attributes from all annotations found, or {@code null} if not found
* @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
public static @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(
public static MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(
AnnotatedElement element, String annotationName) {
return getAllAnnotationAttributes(element, annotationName, false, false);
@ -502,7 +502,7 @@ public abstract class AnnotatedElementUtils { @@ -502,7 +502,7 @@ public abstract class AnnotatedElementUtils {
* @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
* attributes from all annotations found, or {@code null} if not found
*/
public static @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
public static MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(AnnotatedElement element,
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);

14
spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -45,7 +45,7 @@ import org.springframework.util.StringUtils; @@ -45,7 +45,7 @@ import org.springframework.util.StringUtils;
* @see AnnotatedElementUtils
*/
@SuppressWarnings("serial")
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
public class AnnotationAttributes extends LinkedHashMap<String, @Nullable Object> {
private static final String UNKNOWN = "unknown";
@ -83,7 +83,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -83,7 +83,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* @param map original source of annotation attribute <em>key-value</em> pairs
* @see #fromMap(Map)
*/
public AnnotationAttributes(Map<String, Object> map) {
public AnnotationAttributes(Map<String, @Nullable Object> map) {
super(map);
this.annotationType = null;
this.displayName = UNKNOWN;
@ -376,10 +376,10 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -376,10 +376,10 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
@Override
public String toString() {
Iterator<Map.Entry<String, Object>> entries = entrySet().iterator();
Iterator<Map.Entry<String, @Nullable Object>> entries = entrySet().iterator();
StringBuilder sb = new StringBuilder("{");
while (entries.hasNext()) {
Map.Entry<String, Object> entry = entries.next();
Map.Entry<String, @Nullable Object> entry = entries.next();
sb.append(entry.getKey());
sb.append('=');
sb.append(valueToString(entry.getValue()));
@ -391,7 +391,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -391,7 +391,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
return sb.toString();
}
private String valueToString(Object value) {
private String valueToString(@Nullable Object value) {
if (value == this) {
return "(this Map)";
}
@ -410,7 +410,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -410,7 +410,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* to the {@link #AnnotationAttributes(Map)} constructor.
* @param map original source of annotation attribute <em>key-value</em> pairs
*/
public static @Nullable AnnotationAttributes fromMap(@Nullable Map<String, Object> map) {
public static @Nullable AnnotationAttributes fromMap(@Nullable Map<String, @Nullable Object> map) {
if (map == null) {
return null;
}

6
spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -27,6 +27,7 @@ import java.util.Set; @@ -27,6 +27,7 @@ import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
@ -87,7 +88,7 @@ final class AnnotationTypeMappings { @@ -87,7 +88,7 @@ final class AnnotationTypeMappings {
}
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source) {
Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
@Nullable Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
for (Annotation metaAnnotation : metaAnnotations) {
if (!isMappable(source, metaAnnotation)) {
continue;
@ -127,6 +128,7 @@ final class AnnotationTypeMappings { @@ -127,6 +128,7 @@ final class AnnotationTypeMappings {
}
}
@Contract("_, null -> false")
private boolean isMappable(AnnotationTypeMapping source, @Nullable Annotation metaAnnotation) {
return (metaAnnotation != null && !this.filter.matches(metaAnnotation) &&
!AnnotationFilter.PLAIN.matches(source.getAnnotationType()) &&

6
spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -773,7 +773,7 @@ public abstract class AnnotationUtils { @@ -773,7 +773,7 @@ public abstract class AnnotationUtils {
* @see #getAnnotationAttributes(Annotation, boolean, boolean)
* @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
*/
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
public static Map<String, @Nullable Object> getAnnotationAttributes(Annotation annotation) {
return getAnnotationAttributes(null, annotation);
}
@ -791,7 +791,7 @@ public abstract class AnnotationUtils { @@ -791,7 +791,7 @@ public abstract class AnnotationUtils {
* corresponding attribute values as values (never {@code null})
* @see #getAnnotationAttributes(Annotation, boolean, boolean)
*/
public static Map<String, Object> getAnnotationAttributes(
public static Map<String, @Nullable Object> getAnnotationAttributes(
Annotation annotation, boolean classValuesAsString) {
return getAnnotationAttributes(annotation, classValuesAsString, false);

4
spring-core/src/main/java/org/springframework/core/annotation/AnnotationsProcessor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2025 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.
@ -54,7 +54,7 @@ interface AnnotationsProcessor<C, R> { @@ -54,7 +54,7 @@ interface AnnotationsProcessor<C, R> {
* {@code null} elements)
* @return a {@code non-null} result if no further processing is required
*/
@Nullable R doWithAnnotations(C context, int aggregateIndex, @Nullable Object source, Annotation[] annotations);
@Nullable R doWithAnnotations(C context, int aggregateIndex, @Nullable Object source, @Nullable Annotation[] annotations);
/**
* Get the final result to be returned. By default this method returns

32
spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -47,7 +47,7 @@ import org.springframework.util.ReflectionUtils; @@ -47,7 +47,7 @@ import org.springframework.util.ReflectionUtils;
*/
abstract class AnnotationsScanner {
private static final Annotation[] NO_ANNOTATIONS = {};
private static final @Nullable Annotation[] NO_ANNOTATIONS = {};
private static final Method[] NO_METHODS = {};
@ -55,7 +55,7 @@ abstract class AnnotationsScanner { @@ -55,7 +55,7 @@ abstract class AnnotationsScanner {
private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationCache =
new ConcurrentReferenceHashMap<>(256);
private static final Map<Class<?>, Method[]> baseTypeMethodsCache =
private static final Map<Class<?>, @Nullable Method[]> baseTypeMethodsCache =
new ConcurrentReferenceHashMap<>(256);
@ -114,7 +114,7 @@ abstract class AnnotationsScanner { @@ -114,7 +114,7 @@ abstract class AnnotationsScanner {
if (isWithoutHierarchy(source, Search.never)) {
return processElement(context, source, processor);
}
Annotation[] relevant = null;
@Nullable Annotation[] relevant = null;
int remaining = Integer.MAX_VALUE;
int aggregateIndex = 0;
Class<?> root = source;
@ -123,7 +123,7 @@ abstract class AnnotationsScanner { @@ -123,7 +123,7 @@ abstract class AnnotationsScanner {
if (result != null) {
return result;
}
Annotation[] declaredAnns = getDeclaredAnnotations(source, true);
@Nullable Annotation[] declaredAnns = getDeclaredAnnotations(source, true);
if (declaredAnns.length > 0) {
if (relevant == null) {
relevant = root.getAnnotations();
@ -133,6 +133,7 @@ abstract class AnnotationsScanner { @@ -133,6 +133,7 @@ abstract class AnnotationsScanner {
if (declaredAnns[i] != null) {
boolean isRelevant = false;
for (int relevantIndex = 0; relevantIndex < relevant.length; relevantIndex++) {
//noinspection DataFlowIssue
if (relevant[relevantIndex] != null &&
declaredAnns[i].annotationType() == relevant[relevantIndex].annotationType()) {
isRelevant = true;
@ -181,7 +182,7 @@ abstract class AnnotationsScanner { @@ -181,7 +182,7 @@ abstract class AnnotationsScanner {
if (hasPlainJavaAnnotationsOnly(source)) {
return null;
}
Annotation[] annotations = getDeclaredAnnotations(source, false);
@Nullable Annotation[] annotations = getDeclaredAnnotations(source, false);
result = processor.doWithAnnotations(context, aggregateIndex[0], source, annotations);
if (result != null) {
return result;
@ -320,16 +321,18 @@ abstract class AnnotationsScanner { @@ -320,16 +321,18 @@ abstract class AnnotationsScanner {
return null;
}
private static <C> Method[] getBaseTypeMethods(C context, Class<?> baseType) {
@SuppressWarnings("NullAway") // Dataflow analysis limitation
private static <C> @Nullable Method[] getBaseTypeMethods(C context, Class<?> baseType) {
if (baseType == Object.class || hasPlainJavaAnnotationsOnly(baseType)) {
return NO_METHODS;
}
Method[] methods = baseTypeMethodsCache.get(baseType);
@Nullable Method[] methods = baseTypeMethodsCache.get(baseType);
if (methods == null) {
methods = ReflectionUtils.getDeclaredMethods(baseType);
int cleared = 0;
for (int i = 0; i < methods.length; i++) {
//noinspection DataFlowIssue
if (Modifier.isPrivate(methods[i].getModifiers()) ||
hasPlainJavaAnnotationsOnly(methods[i]) ||
getDeclaredAnnotations(methods[i], false).length == 0) {
@ -385,14 +388,14 @@ abstract class AnnotationsScanner { @@ -385,14 +388,14 @@ abstract class AnnotationsScanner {
private static <C, R> @Nullable R processMethodAnnotations(C context, int aggregateIndex, Method source,
AnnotationsProcessor<C, R> processor) {
Annotation[] annotations = getDeclaredAnnotations(source, false);
@Nullable Annotation[] annotations = getDeclaredAnnotations(source, false);
R result = processor.doWithAnnotations(context, aggregateIndex, source, annotations);
if (result != null) {
return result;
}
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(source);
if (bridgedMethod != source) {
Annotation[] bridgedAnnotations = getDeclaredAnnotations(bridgedMethod, true);
@Nullable Annotation[] bridgedAnnotations = getDeclaredAnnotations(bridgedMethod, true);
for (int i = 0; i < bridgedAnnotations.length; i++) {
if (ObjectUtils.containsElement(annotations, bridgedAnnotations[i])) {
bridgedAnnotations[i] = null;
@ -419,7 +422,7 @@ abstract class AnnotationsScanner { @@ -419,7 +422,7 @@ abstract class AnnotationsScanner {
@SuppressWarnings("unchecked")
static <A extends Annotation> @Nullable A getDeclaredAnnotation(AnnotatedElement source, Class<A> annotationType) {
Annotation[] annotations = getDeclaredAnnotations(source, false);
@Nullable Annotation[] annotations = getDeclaredAnnotations(source, false);
for (Annotation annotation : annotations) {
if (annotation != null && annotationType == annotation.annotationType()) {
return (A) annotation;
@ -428,9 +431,10 @@ abstract class AnnotationsScanner { @@ -428,9 +431,10 @@ abstract class AnnotationsScanner {
return null;
}
static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) {
@SuppressWarnings("NullAway") // Dataflow analysis limitation
static @Nullable Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) {
boolean cached = false;
Annotation[] annotations = declaredAnnotationCache.get(source);
@Nullable Annotation[] annotations = declaredAnnotationCache.get(source);
if (annotations != null) {
cached = true;
}
@ -440,6 +444,7 @@ abstract class AnnotationsScanner { @@ -440,6 +444,7 @@ abstract class AnnotationsScanner {
boolean allIgnored = true;
for (int i = 0; i < annotations.length; i++) {
Annotation annotation = annotations[i];
//noinspection DataFlowIssue
if (isIgnorable(annotation.annotationType()) ||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
annotations[i] = null;
@ -450,6 +455,7 @@ abstract class AnnotationsScanner { @@ -450,6 +455,7 @@ abstract class AnnotationsScanner {
}
annotations = (allIgnored ? NO_ANNOTATIONS : annotations);
if (source instanceof Class || source instanceof Member) {
//noinspection NullableProblems
declaredAnnotationCache.put(source, annotations);
cached = true;
}

4
spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -250,11 +250,13 @@ final class AttributeMethods { @@ -250,11 +250,13 @@ final class AttributeMethods {
return cache.computeIfAbsent(annotationType, AttributeMethods::compute);
}
@SuppressWarnings("NullAway") // Dataflow analysis limitation
private static AttributeMethods compute(Class<? extends Annotation> annotationType) {
Method[] methods = annotationType.getDeclaredMethods();
int size = methods.length;
for (int i = 0; i < methods.length; i++) {
if (!isAttributeMethod(methods[i])) {
//noinspection DataFlowIssue
methods[i] = null;
size--;
}

16
spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationCollectors.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 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.
@ -26,6 +26,8 @@ import java.util.function.IntFunction; @@ -26,6 +26,8 @@ import java.util.function.IntFunction;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@ -108,10 +110,10 @@ public abstract class MergedAnnotationCollectors { @@ -108,10 +110,10 @@ public abstract class MergedAnnotationCollectors {
* annotations into a {@link LinkedMultiValueMap}
* @see #toMultiValueMap(Function, MergedAnnotation.Adapt...)
*/
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap(
public static <A extends Annotation> Collector<MergedAnnotation<A>, ? extends @Nullable Object, @Nullable MultiValueMap<String, @Nullable Object>> toMultiValueMap(
Adapt... adaptations) {
return toMultiValueMap(Function.identity(), adaptations);
return toMultiValueMap((MultiValueMap<String, @Nullable Object> t) -> t, adaptations);
}
/**
@ -126,14 +128,14 @@ public abstract class MergedAnnotationCollectors { @@ -126,14 +128,14 @@ public abstract class MergedAnnotationCollectors {
* annotations into a {@link LinkedMultiValueMap}
* @see #toMultiValueMap(MergedAnnotation.Adapt...)
*/
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap(
Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher,
public static <A extends Annotation> Collector<MergedAnnotation<A>, ? extends @Nullable Object, @Nullable MultiValueMap<String, @Nullable Object>> toMultiValueMap(
Function<MultiValueMap<String, @Nullable Object>, @Nullable MultiValueMap<String, @Nullable Object>> finisher,
Adapt... adaptations) {
Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ?
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
return Collector.of(LinkedMultiValueMap::new,
(map, annotation) -> annotation.asMap(adaptations).forEach(map::add),
(MultiValueMap<String, @Nullable Object> map, MergedAnnotation<A> annotation) -> annotation.asMap(adaptations).forEach(map::add),
MergedAnnotationCollectors::combiner, finisher, characteristics);
}
@ -157,7 +159,7 @@ public abstract class MergedAnnotationCollectors { @@ -157,7 +159,7 @@ public abstract class MergedAnnotationCollectors {
* <p>This method is only invoked if the {@link java.util.stream.Stream} is
* processed in {@linkplain java.util.stream.Stream#parallel() parallel}.
*/
private static <K, V> MultiValueMap<K, V> combiner(MultiValueMap<K, V> map, MultiValueMap<K, V> additions) {
private static <K, V> MultiValueMap<K, @Nullable V> combiner(MultiValueMap<K, @Nullable V> map, MultiValueMap<K, @Nullable V> additions) {
map.addAll(additions);
return map;
}

11
spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
@ -23,8 +23,6 @@ import java.util.Set; @@ -23,8 +23,6 @@ import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@ -126,7 +124,8 @@ public abstract class MergedAnnotationPredicates { @@ -126,7 +124,8 @@ public abstract class MergedAnnotationPredicates {
private boolean hasLastValue;
private @Nullable Object lastValue;
@SuppressWarnings("NullAway.Init")
private Object lastValue;
FirstRunOfPredicate(Function<? super MergedAnnotation<A>, ?> valueExtractor) {
Assert.notNull(valueExtractor, "Value extractor must not be null");
@ -134,7 +133,7 @@ public abstract class MergedAnnotationPredicates { @@ -134,7 +133,7 @@ public abstract class MergedAnnotationPredicates {
}
@Override
public boolean test(@Nullable MergedAnnotation<A> annotation) {
public boolean test(MergedAnnotation<A> annotation) {
if (!this.hasLastValue) {
this.hasLastValue = true;
this.lastValue = this.valueExtractor.apply(annotation);
@ -162,7 +161,7 @@ public abstract class MergedAnnotationPredicates { @@ -162,7 +161,7 @@ public abstract class MergedAnnotationPredicates {
}
@Override
public boolean test(@Nullable MergedAnnotation<A> annotation) {
public boolean test(MergedAnnotation<A> annotation) {
K key = this.keyExtractor.apply(annotation);
return this.seen.add(key);
}

17
spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotations.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -29,6 +29,7 @@ import java.util.function.Predicate; @@ -29,6 +29,7 @@ import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
/**
@ -309,7 +310,7 @@ final class TypeMappedAnnotations implements MergedAnnotations { @@ -309,7 +310,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Override
public @Nullable Boolean doWithAnnotations(Object requiredType, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) {
@Nullable Object source, @Nullable Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation != null) {
@ -388,7 +389,7 @@ final class TypeMappedAnnotations implements MergedAnnotations { @@ -388,7 +389,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Override
public @Nullable MergedAnnotation<A> doWithAnnotations(Object type, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) {
@Nullable Object source, @Nullable Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation != null && !annotationFilter.matches(annotation)) {
@ -450,24 +451,24 @@ final class TypeMappedAnnotations implements MergedAnnotations { @@ -450,24 +451,24 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Override
public @Nullable List<Aggregate> doWithAnnotations(Object criteria, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) {
@Nullable Object source, @Nullable Annotation[] annotations) {
this.aggregates.add(createAggregate(aggregateIndex, source, annotations));
return null;
}
private Aggregate createAggregate(int aggregateIndex, @Nullable Object source, Annotation[] annotations) {
private Aggregate createAggregate(int aggregateIndex, @Nullable Object source, @Nullable Annotation[] annotations) {
List<Annotation> aggregateAnnotations = getAggregateAnnotations(annotations);
return new Aggregate(aggregateIndex, source, aggregateAnnotations);
}
private List<Annotation> getAggregateAnnotations(Annotation[] annotations) {
private List<Annotation> getAggregateAnnotations(@Nullable Annotation[] annotations) {
List<Annotation> result = new ArrayList<>(annotations.length);
addAggregateAnnotations(result, annotations);
return result;
}
private void addAggregateAnnotations(List<Annotation> aggregateAnnotations, Annotation[] annotations) {
private void addAggregateAnnotations(List<Annotation> aggregateAnnotations, @Nullable Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation != null && !annotationFilter.matches(annotation)) {
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
@ -482,7 +483,7 @@ final class TypeMappedAnnotations implements MergedAnnotations { @@ -482,7 +483,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
}
@Override
public List<Aggregate> finish(@Nullable List<Aggregate> processResult) {
public @NonNull List<Aggregate> finish(@Nullable List<Aggregate> processResult) {
return this.aggregates;
}
}

6
spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -584,11 +584,11 @@ public class SpringFactoriesLoader { @@ -584,11 +584,11 @@ public class SpringFactoriesLoader {
* @param function the resolver function
* @return a new {@link ArgumentResolver} instance backed by the function
*/
static ArgumentResolver from(Function<Class<?>, Object> function) {
static ArgumentResolver from(Function<Class<?>, @Nullable Object> function) {
return new ArgumentResolver() {
@SuppressWarnings("unchecked")
@Override
public <T> T resolve(Class<T> type) {
public <T> @Nullable T resolve(Class<T> type) {
return (T) function.apply(type);
}
};

12
spring-core/src/main/java/org/springframework/core/type/AnnotatedTypeMetadata.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -87,7 +87,7 @@ public interface AnnotatedTypeMetadata { @@ -87,7 +87,7 @@ public interface AnnotatedTypeMetadata {
* as map key (for example, "location") and the attribute's value as map value; or
* {@code null} if no matching annotation is found
*/
default @Nullable Map<String, Object> getAnnotationAttributes(String annotationName) {
default @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName) {
return getAnnotationAttributes(annotationName, false);
}
@ -106,7 +106,7 @@ public interface AnnotatedTypeMetadata { @@ -106,7 +106,7 @@ public interface AnnotatedTypeMetadata {
* as map key (for example, "location") and the attribute's value as map value; or
* {@code null} if no matching annotation is found
*/
default @Nullable Map<String, Object> getAnnotationAttributes(String annotationName,
default @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName,
boolean classValuesAsString) {
MergedAnnotation<Annotation> annotation = getAnnotations().get(annotationName,
@ -129,7 +129,7 @@ public interface AnnotatedTypeMetadata { @@ -129,7 +129,7 @@ public interface AnnotatedTypeMetadata {
* map value; or {@code null} if no matching annotation is found
* @see #getAllAnnotationAttributes(String, boolean)
*/
default @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
default @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(String annotationName) {
return getAllAnnotationAttributes(annotationName, false);
}
@ -148,7 +148,7 @@ public interface AnnotatedTypeMetadata { @@ -148,7 +148,7 @@ public interface AnnotatedTypeMetadata {
* map value; or {@code null} if no matching annotation is found
* @see #getAllAnnotationAttributes(String)
*/
default @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(
default @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(
String annotationName, boolean classValuesAsString) {
Adapt[] adaptations = Adapt.values(classValuesAsString, true);
@ -156,7 +156,7 @@ public interface AnnotatedTypeMetadata { @@ -156,7 +156,7 @@ public interface AnnotatedTypeMetadata {
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(
map -> (map.isEmpty() ? null : map), adaptations));
(MultiValueMap<String, @Nullable Object> map) -> (map.isEmpty() ? null : map), adaptations));
}
/**

7
spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2025 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.
@ -105,7 +105,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements @@ -105,7 +105,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
}
@Override
public @Nullable Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
public @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) {
return AnnotationMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString);
}
@ -114,7 +114,8 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements @@ -114,7 +114,8 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
}
@Override
public @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
@SuppressWarnings("NullAway") // Null-safety of Java super method not yet managed
public @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) {
return AnnotationMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString);
}

7
spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -131,7 +131,7 @@ public class StandardMethodMetadata implements MethodMetadata { @@ -131,7 +131,7 @@ public class StandardMethodMetadata implements MethodMetadata {
}
@Override
public @Nullable Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
public @Nullable Map<String, @Nullable Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) {
return MethodMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString);
}
@ -140,7 +140,8 @@ public class StandardMethodMetadata implements MethodMetadata { @@ -140,7 +140,8 @@ public class StandardMethodMetadata implements MethodMetadata {
}
@Override
public @Nullable MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
@SuppressWarnings("NullAway") // Null-safety of Java super method not yet managed
public @Nullable MultiValueMap<String, @Nullable Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (this.nestedAnnotationsAsMap) {
return MethodMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString);
}

6
spring-core/src/main/java/org/springframework/util/CollectionUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -64,7 +64,7 @@ public abstract class CollectionUtils { @@ -64,7 +64,7 @@ public abstract class CollectionUtils {
* @return whether the given Collection is empty
*/
@Contract("null -> true")
public static boolean isEmpty(@Nullable Collection<?> collection) {
public static boolean isEmpty(@Nullable Collection<? extends @Nullable Object> collection) {
return (collection == null || collection.isEmpty());
}
@ -75,7 +75,7 @@ public abstract class CollectionUtils { @@ -75,7 +75,7 @@ public abstract class CollectionUtils {
* @return whether the given Map is empty
*/
@Contract("null -> true")
public static boolean isEmpty(@Nullable Map<?, ?> map) {
public static boolean isEmpty(@Nullable Map<?, ? extends @Nullable Object> map) {
return (map == null || map.isEmpty());
}

10
spring-core/src/main/java/org/springframework/util/CompositeCollection.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -20,6 +20,8 @@ import java.lang.reflect.Array; @@ -20,6 +20,8 @@ import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import org.jspecify.annotations.Nullable;
/**
* Composite collection that combines two other collections. This type is only
@ -83,10 +85,10 @@ class CompositeCollection<E> implements Collection<E> { @@ -83,10 +85,10 @@ class CompositeCollection<E> implements Collection<E> {
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
@SuppressWarnings({"unchecked","NullAway"}) // Overridden method does not define nullness
public <T> @Nullable T[] toArray(@Nullable T[] a) {
int size = this.size();
T[] result;
@Nullable T[] result;
if (a.length >= size) {
result = a;
}

12
spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -456,7 +456,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen @@ -456,7 +456,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
* Array of references indexed using the low order bits from the hash.
* This property should only be set along with {@code resizeThreshold}.
*/
private volatile Reference<K, V>[] references;
private volatile @Nullable Reference<K, V>[] references;
/**
* The total number of references contained in this segment. This includes chained
@ -485,7 +485,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen @@ -485,7 +485,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
return null;
}
// Use a local copy to protect against other threads writing
Reference<K, V>[] references = this.references;
@Nullable Reference<K, V>[] references = this.references;
int index = getIndex(hash, references);
Reference<K, V> head = references[index];
return findInChain(head, key, hash);
@ -641,7 +641,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen @@ -641,7 +641,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
}
}
private @Nullable Reference<K, V> findInChain(Reference<K, V> ref, @Nullable Object key, int hash) {
private @Nullable Reference<K, V> findInChain(@Nullable Reference<K, V> ref, @Nullable Object key, int hash) {
Reference<K, V> currRef = ref;
while (currRef != null) {
if (currRef.getHash() == hash) {
@ -663,7 +663,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen @@ -663,7 +663,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
return new Reference[size];
}
private int getIndex(int hash, Reference<K, V>[] references) {
private int getIndex(int hash, @Nullable Reference<K, V>[] references) {
return (hash & (references.length - 1));
}
@ -879,7 +879,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen @@ -879,7 +879,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
private int referenceIndex;
private Reference<K, V> @Nullable [] references;
private @Nullable Reference<K, V> @Nullable [] references;
private @Nullable Reference<K, V> reference;

12
spring-core/src/main/java/org/springframework/util/MethodInvoker.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -49,7 +49,7 @@ public class MethodInvoker { @@ -49,7 +49,7 @@ public class MethodInvoker {
private @Nullable String staticMethod;
private Object @Nullable [] arguments;
private @Nullable Object @Nullable [] arguments;
/** The method we will call. */
private @Nullable Method methodObject;
@ -127,14 +127,14 @@ public class MethodInvoker { @@ -127,14 +127,14 @@ public class MethodInvoker {
* Set arguments for the method invocation. If this property is not set,
* or the Object array is of length 0, a method with no arguments is assumed.
*/
public void setArguments(Object @Nullable ... arguments) {
public void setArguments(@Nullable Object... arguments) {
this.arguments = arguments;
}
/**
* Return the arguments for the method invocation.
*/
public Object[] getArguments() {
public @Nullable Object[] getArguments() {
return (this.arguments != null ? this.arguments : EMPTY_ARGUMENTS);
}
@ -166,7 +166,7 @@ public class MethodInvoker { @@ -166,7 +166,7 @@ public class MethodInvoker {
Assert.notNull(targetClass, "Either 'targetClass' or 'targetObject' is required");
Assert.notNull(targetMethod, "Property 'targetMethod' is required");
Object[] arguments = getArguments();
@Nullable Object[] arguments = getArguments();
Class<?>[] argTypes = new Class<?>[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
argTypes[i] = (arguments[i] != null ? arguments[i].getClass() : Object.class);
@ -206,7 +206,7 @@ public class MethodInvoker { @@ -206,7 +206,7 @@ public class MethodInvoker {
*/
protected @Nullable Method findMatchingMethod() {
String targetMethod = getTargetMethod();
Object[] arguments = getArguments();
@Nullable Object[] arguments = getArguments();
int argCount = arguments.length;
Class<?> targetClass = getTargetClass();

4
spring-core/src/main/java/org/springframework/util/MultiValueMap.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -29,7 +29,7 @@ import org.jspecify.annotations.Nullable; @@ -29,7 +29,7 @@ import org.jspecify.annotations.Nullable;
* @param <K> the key type
* @param <V> the value element type
*/
public interface MultiValueMap<K, V> extends Map<K, List<V>> {
public interface MultiValueMap<K, V extends @Nullable Object> extends Map<K, List<V>> {
/**
* Return the first value for the given key.

10
spring-core/src/main/java/org/springframework/util/ObjectUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -188,7 +188,7 @@ public abstract class ObjectUtils { @@ -188,7 +188,7 @@ public abstract class ObjectUtils {
* @param element the element to check for
* @return whether the element has been found in the given array
*/
public static boolean containsElement(Object @Nullable [] array, Object element) {
public static boolean containsElement(@Nullable Object @Nullable [] array, @Nullable Object element) {
if (array == null) {
return false;
}
@ -266,7 +266,7 @@ public abstract class ObjectUtils { @@ -266,7 +266,7 @@ public abstract class ObjectUtils {
* @return the new array (of the same component type; never {@code null})
* @since 6.0
*/
public static <A, O extends A> A[] addObjectToArray(A @Nullable [] array, @Nullable O obj, int position) {
public static <A, O extends A> @Nullable A[] addObjectToArray(A @Nullable [] array, @Nullable O obj, int position) {
Class<?> componentType = Object.class;
if (array != null) {
componentType = array.getClass().componentType();
@ -276,7 +276,7 @@ public abstract class ObjectUtils { @@ -276,7 +276,7 @@ public abstract class ObjectUtils {
}
int newArrayLength = (array != null ? array.length + 1 : 1);
@SuppressWarnings("unchecked")
A[] newArray = (A[]) Array.newInstance(componentType, newArrayLength);
@Nullable A[] newArray = (A[]) Array.newInstance(componentType, newArrayLength);
if (array != null) {
System.arraycopy(array, 0, newArray, 0, position);
System.arraycopy(array, position, newArray, position + 1, array.length - position);
@ -651,7 +651,7 @@ public abstract class ObjectUtils { @@ -651,7 +651,7 @@ public abstract class ObjectUtils {
* @param array the array to build a String representation for
* @return a String representation of {@code array}
*/
public static String nullSafeToString(Object @Nullable [] array) {
public static String nullSafeToString(@Nullable Object @Nullable [] array) {
if (array == null) {
return NULL_STRING;
}

6
spring-core/src/main/java/org/springframework/util/StringUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -1038,12 +1038,12 @@ public abstract class StringUtils { @@ -1038,12 +1038,12 @@ public abstract class StringUtils {
* @param array the original {@code String} array (potentially empty)
* @return the resulting array (of the same size) with trimmed elements
*/
public static String[] trimArrayElements(String[] array) {
public static @Nullable String[] trimArrayElements(@Nullable String[] array) {
if (ObjectUtils.isEmpty(array)) {
return array;
}
String[] result = new String[array.length];
@Nullable String[] result = new String[array.length];
for (int i = 0; i < array.length; i++) {
String element = array[i];
result[i] = (element != null ? element.trim() : null);

21
spring-core/src/main/java/org/springframework/util/function/SingletonSupplier.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -22,6 +22,7 @@ import java.util.function.Supplier; @@ -22,6 +22,7 @@ import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
/**
@ -38,11 +39,11 @@ import org.springframework.util.Assert; @@ -38,11 +39,11 @@ import org.springframework.util.Assert;
* @since 5.1
* @param <T> the type of results supplied by this supplier
*/
public class SingletonSupplier<T> implements Supplier<T> {
public class SingletonSupplier<T> implements Supplier<@Nullable T> {
private final @Nullable Supplier<? extends T> instanceSupplier;
private final @Nullable Supplier<? extends @Nullable T> instanceSupplier;
private final @Nullable Supplier<? extends T> defaultSupplier;
private final @Nullable Supplier<? extends @Nullable T> defaultSupplier;
private volatile @Nullable T singletonInstance;
@ -58,7 +59,7 @@ public class SingletonSupplier<T> implements Supplier<T> { @@ -58,7 +59,7 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param instance the singleton instance (potentially {@code null})
* @param defaultSupplier the default supplier as a fallback
*/
public SingletonSupplier(@Nullable T instance, Supplier<? extends T> defaultSupplier) {
public SingletonSupplier(@Nullable T instance, Supplier<? extends @Nullable T> defaultSupplier) {
this.instanceSupplier = null;
this.defaultSupplier = defaultSupplier;
this.singletonInstance = instance;
@ -70,17 +71,17 @@ public class SingletonSupplier<T> implements Supplier<T> { @@ -70,17 +71,17 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param instanceSupplier the immediate instance supplier
* @param defaultSupplier the default supplier as a fallback
*/
public SingletonSupplier(@Nullable Supplier<? extends T> instanceSupplier, Supplier<? extends T> defaultSupplier) {
public SingletonSupplier(@Nullable Supplier<? extends @Nullable T> instanceSupplier, Supplier<? extends @Nullable T> defaultSupplier) {
this.instanceSupplier = instanceSupplier;
this.defaultSupplier = defaultSupplier;
}
private SingletonSupplier(Supplier<? extends T> supplier) {
private SingletonSupplier(Supplier<? extends @Nullable T> supplier) {
this.instanceSupplier = supplier;
this.defaultSupplier = null;
}
private SingletonSupplier(T singletonInstance) {
private SingletonSupplier(@Nullable T singletonInstance) {
this.instanceSupplier = null;
this.defaultSupplier = null;
this.singletonInstance = singletonInstance;
@ -141,6 +142,7 @@ public class SingletonSupplier<T> implements Supplier<T> { @@ -141,6 +142,7 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param instance the singleton instance (potentially {@code null})
* @return the singleton supplier, or {@code null} if the instance was {@code null}
*/
@Contract("null -> null; !null -> !null")
public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable T instance) {
return (instance != null ? new SingletonSupplier<>(instance) : null);
}
@ -159,7 +161,8 @@ public class SingletonSupplier<T> implements Supplier<T> { @@ -159,7 +161,8 @@ public class SingletonSupplier<T> implements Supplier<T> {
* @param supplier the instance supplier (potentially {@code null})
* @return the singleton supplier, or {@code null} if the instance supplier was {@code null}
*/
public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable Supplier<T> supplier) {
@Contract("null -> null; !null -> !null")
public static <T> @Nullable SingletonSupplier<T> ofNullable(@Nullable Supplier<? extends @Nullable T> supplier) {
return (supplier != null ? new SingletonSupplier<>(supplier) : null);
}

5
spring-core/src/main/java/org/springframework/util/function/SupplierUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -20,6 +20,8 @@ import java.util.function.Supplier; @@ -20,6 +20,8 @@ import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
/**
* Convenience utilities for {@link java.util.function.Supplier} handling.
*
@ -35,6 +37,7 @@ public abstract class SupplierUtils { @@ -35,6 +37,7 @@ public abstract class SupplierUtils {
* @param supplier the supplier to resolve
* @return the supplier's result, or {@code null} if none
*/
@Contract("null -> null; !null -> !null")
public static <T> @Nullable T resolve(@Nullable Supplier<T> supplier) {
return (supplier != null ? supplier.get() : null);
}

15
spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 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.
@ -29,6 +29,7 @@ import java.util.Objects; @@ -29,6 +29,7 @@ import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
@ -469,13 +470,13 @@ class AnnotationsScannerTests { @@ -469,13 +470,13 @@ class AnnotationsScannerTests {
new AnnotationsProcessor<Object, String>() {
@Override
public String doWithAggregate(Object context, int aggregateIndex) {
public @NonNull String doWithAggregate(Object context, int aggregateIndex) {
return "";
}
@Override
public String doWithAnnotations(Object context, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) {
public @NonNull String doWithAnnotations(Object context, int aggregateIndex,
@Nullable Object source, @Nullable Annotation @Nullable [] annotations) {
throw new IllegalStateException("Should not call");
}
@ -501,13 +502,13 @@ class AnnotationsScannerTests { @@ -501,13 +502,13 @@ class AnnotationsScannerTests {
new AnnotationsProcessor<Object, String>() {
@Override
public String doWithAnnotations(Object context, int aggregateIndex,
@Nullable Object source, Annotation[] annotations) {
public @NonNull String doWithAnnotations(Object context, int aggregateIndex,
@Nullable Object source, @Nullable Annotation @Nullable [] annotations) {
return "K";
}
@Override
public String finish(@Nullable String result) {
public @NonNull String finish(@Nullable String result) {
return "O" + result;
}

Loading…
Cancel
Save