Switch to JSpecify annotations
This commit updates the whole Spring Framework codebase to use JSpecify annotations instead of Spring null-safety annotations with JSR 305 semantics. JSpecify provides signficant enhancements such as properly defined specifications, a canonical dependency with no split-package issue, better tooling, better Kotlin integration and the capability to specify generic type, array and varargs element null-safety. Generic type null-safety is not defined by this commit yet and will be specified later. A key difference is that Spring null-safety annotations, following JSR 305 semantics, apply to fields, parameters and return values, while JSpecify annotations apply to type usages. That's why this commit moves nullability annotations closer to the type for fields and return values. See gh-28797
This commit is contained in:
@@ -31,7 +31,7 @@ import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Instrumented version of JDK methods to be used by bytecode rewritten by the {@link RuntimeHintsAgent}.
|
||||
@@ -232,8 +232,7 @@ public abstract class InstrumentedBridgeMethods {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static URL classgetResource(Class<?> clazz, String name) {
|
||||
public static @Nullable URL classgetResource(Class<?> clazz, String name) {
|
||||
URL result = clazz.getResource(name);
|
||||
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETRESOURCE)
|
||||
.onInstance(clazz).withArgument(name).returnValue(result).build();
|
||||
@@ -241,8 +240,7 @@ public abstract class InstrumentedBridgeMethods {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InputStream classgetResourceAsStream(Class<?> clazz, String name) {
|
||||
public static @Nullable InputStream classgetResourceAsStream(Class<?> clazz, String name) {
|
||||
InputStream result = clazz.getResourceAsStream(name);
|
||||
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETRESOURCEASSTREAM)
|
||||
.onInstance(clazz).withArgument(name).returnValue(result).build();
|
||||
@@ -267,8 +265,7 @@ public abstract class InstrumentedBridgeMethods {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static URL classloadergetResource(ClassLoader classLoader, String name) {
|
||||
public static @Nullable URL classloadergetResource(ClassLoader classLoader, String name) {
|
||||
URL result = classLoader.getResource(name);
|
||||
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASSLOADER_GETRESOURCE)
|
||||
.onInstance(classLoader).withArgument(name).returnValue(result).build();
|
||||
@@ -276,8 +273,7 @@ public abstract class InstrumentedBridgeMethods {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InputStream classloadergetResourceAsStream(ClassLoader classLoader, String name) {
|
||||
public static @Nullable InputStream classloadergetResourceAsStream(ClassLoader classLoader, String name) {
|
||||
InputStream result = classLoader.getResourceAsStream(name);
|
||||
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASSLOADER_GETRESOURCEASSTREAM)
|
||||
.onInstance(classLoader).withArgument(name).returnValue(result).build();
|
||||
|
||||
@@ -21,8 +21,9 @@ import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.aot.agent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Reference to a Java method, identified by its owner class and the method name.
|
||||
|
||||
@@ -20,9 +20,10 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -36,15 +37,13 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public final class RecordedInvocation {
|
||||
|
||||
@Nullable
|
||||
private final Object instance;
|
||||
private final @Nullable Object instance;
|
||||
|
||||
private final InstrumentedMethod instrumentedMethod;
|
||||
|
||||
private final Object[] arguments;
|
||||
|
||||
@Nullable
|
||||
private final Object returnValue;
|
||||
private final @Nullable Object returnValue;
|
||||
|
||||
private final List<StackWalker.StackFrame> stackFrames;
|
||||
|
||||
@@ -160,8 +159,7 @@ public final class RecordedInvocation {
|
||||
* @return the value returned by the invocation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T> T getReturnValue() {
|
||||
public <T> @Nullable T getReturnValue() {
|
||||
return (T) this.returnValue;
|
||||
}
|
||||
|
||||
@@ -192,15 +190,13 @@ public final class RecordedInvocation {
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
@Nullable
|
||||
private Object instance;
|
||||
private @Nullable Object instance;
|
||||
|
||||
private final InstrumentedMethod instrumentedMethod;
|
||||
|
||||
private Object[] arguments = new Object[0];
|
||||
|
||||
@Nullable
|
||||
private Object returnValue;
|
||||
private @Nullable Object returnValue;
|
||||
|
||||
|
||||
Builder(InstrumentedMethod instrumentedMethod) {
|
||||
@@ -234,7 +230,7 @@ public final class RecordedInvocation {
|
||||
* @param arguments the invocation arguments
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder withArguments(@Nullable Object... arguments) {
|
||||
public Builder withArguments(Object @Nullable ... arguments) {
|
||||
if (arguments != null) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ import java.lang.instrument.Instrumentation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* Support for recording method invocations relevant to {@link org.springframework.aot.hint.RuntimeHints} metadata.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
@NullMarked
|
||||
package org.springframework.aot.agent;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* Testing support for the {@link org.springframework.aot.agent.RuntimeHintsAgent}.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
@NullMarked
|
||||
package org.springframework.aot.test.agent;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* Test support for core AOT classes.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
@NullMarked
|
||||
package org.springframework.aot.test.generate;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -24,8 +24,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Simple mock {@link SpringFactoriesLoader} implementation that can be used for testing
|
||||
@@ -67,9 +68,8 @@ public class MockSpringFactoriesLoader extends SpringFactoriesLoader {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T instantiateFactory(String implementationName, Class<T> type,
|
||||
protected <T> @Nullable T instantiateFactory(String implementationName, Class<T> type,
|
||||
@Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) {
|
||||
if (implementationName.startsWith("!")) {
|
||||
Object implementation = this.implementations.get(implementationName);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* Test support classes for Spring's I/O support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
@NullMarked
|
||||
package org.springframework.core.test.io.support;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An immutable collection of {@link ClassFile} instances.
|
||||
@@ -112,8 +112,7 @@ public final class ClassFiles implements Iterable<ClassFile> {
|
||||
* @param name the fully qualified name to find
|
||||
* @return a {@link ClassFile} instance or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public ClassFile get(String name) {
|
||||
public @Nullable ClassFile get(String name) {
|
||||
return this.files.get(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* {@link ClassLoader} implementation to support
|
||||
@@ -73,8 +73,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader {
|
||||
return (bytes != null ? defineClass(name, bytes, 0, bytes.length, null) : super.findClass(name));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private byte[] findClassBytes(String name) {
|
||||
private byte @Nullable [] findClassBytes(String name) {
|
||||
byte[] bytes = this.classResourceLookup.apply(name);
|
||||
if (bytes != null) {
|
||||
return bytes;
|
||||
@@ -98,8 +97,7 @@ final class CompileWithForkedClassLoaderClassLoader extends ClassLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected URL findResource(String name) {
|
||||
protected @Nullable URL findResource(String name) {
|
||||
return this.testClassLoader.getResource(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -38,8 +39,7 @@ public class Compiled {
|
||||
|
||||
private final ResourceFiles resourceFiles;
|
||||
|
||||
@Nullable
|
||||
private List<Class<?>> compiledClasses;
|
||||
private @Nullable List<Class<?>> compiledClasses;
|
||||
|
||||
|
||||
Compiled(ClassLoader classLoader, SourceFiles sourceFiles, ResourceFiles resourceFiles) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.net.URI;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* In-memory {@link JavaFileObject} used to hold class bytecode.
|
||||
@@ -38,8 +38,7 @@ class DynamicClassFileObject extends SimpleJavaFileObject {
|
||||
|
||||
private final String className;
|
||||
|
||||
@Nullable
|
||||
private volatile byte[] bytes;
|
||||
private volatile byte @Nullable [] bytes;
|
||||
|
||||
|
||||
DynamicClassFileObject(String className) {
|
||||
@@ -80,8 +79,7 @@ class DynamicClassFileObject extends SimpleJavaFileObject {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
byte[] getBytes() {
|
||||
byte @Nullable [] getBytes() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -52,8 +53,7 @@ public class DynamicClassLoader extends ClassLoader {
|
||||
|
||||
private final Map<String, DynamicResourceFileObject> dynamicResourceFiles;
|
||||
|
||||
@Nullable
|
||||
private final Method defineClassMethod;
|
||||
private final @Nullable Method defineClassMethod;
|
||||
|
||||
|
||||
public DynamicClassLoader(ClassLoader parent, ClassFiles classFiles, ResourceFiles resourceFiles,
|
||||
@@ -89,8 +89,7 @@ public class DynamicClassLoader extends ClassLoader {
|
||||
return (clazz != null ? clazz : super.findClass(name));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Class<?> defineClass(String name, @Nullable byte[] bytes) {
|
||||
private @Nullable Class<?> defineClass(String name, byte @Nullable [] bytes) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -111,8 +110,7 @@ public class DynamicClassLoader extends ClassLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected URL findResource(String name) {
|
||||
protected @Nullable URL findResource(String name) {
|
||||
if (name.endsWith(ClassUtils.CLASS_FILE_SUFFIX)) {
|
||||
String className = ClassUtils.convertResourcePathToClassName(name.substring(0,
|
||||
name.length() - ClassUtils.CLASS_FILE_SUFFIX.length()));
|
||||
@@ -132,8 +130,7 @@ public class DynamicClassLoader extends ClassLoader {
|
||||
return super.findResource(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private byte[] findClassBytes(String name) {
|
||||
private byte @Nullable [] findClassBytes(String name) {
|
||||
ClassFile classFile = this.classFiles.get(name);
|
||||
if (classFile != null) {
|
||||
return classFile.getContent();
|
||||
@@ -162,8 +159,7 @@ public class DynamicClassLoader extends ClassLoader {
|
||||
|
||||
private static class SingletonEnumeration<E> implements Enumeration<E> {
|
||||
|
||||
@Nullable
|
||||
private E element;
|
||||
private @Nullable E element;
|
||||
|
||||
|
||||
SingletonEnumeration(@Nullable E element) {
|
||||
@@ -177,8 +173,7 @@ public class DynamicClassLoader extends ClassLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public E nextElement() {
|
||||
public @Nullable E nextElement() {
|
||||
E next = this.element;
|
||||
this.element = null;
|
||||
return next;
|
||||
|
||||
@@ -19,7 +19,8 @@ package org.springframework.core.test.tools;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
package org.springframework.core.test.tools;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Internal class used by {@link SourceFiles} and {@link ResourceFiles} to
|
||||
@@ -83,8 +83,7 @@ final class DynamicFiles<F extends DynamicFile> implements Iterable<F> {
|
||||
return this.files.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
F get(String path) {
|
||||
@Nullable F get(String path) {
|
||||
return this.files.get(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.net.URI;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* In-memory {@link JavaFileObject} used to hold generated resource file contents.
|
||||
@@ -37,8 +37,7 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
class DynamicResourceFileObject extends SimpleJavaFileObject {
|
||||
|
||||
@Nullable
|
||||
private volatile byte[] bytes;
|
||||
private volatile byte @Nullable [] bytes;
|
||||
|
||||
|
||||
DynamicResourceFileObject(String fileName) {
|
||||
@@ -73,8 +72,7 @@ class DynamicResourceFileObject extends SimpleJavaFileObject {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
byte[] getBytes() {
|
||||
byte @Nullable [] getBytes() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.core.test.tools;
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An immutable collection of {@link ResourceFile} instances.
|
||||
@@ -115,8 +115,7 @@ public final class ResourceFiles implements Iterable<ResourceFile> {
|
||||
* @param path the path to find
|
||||
* @return a {@link ResourceFile} instance or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public ResourceFile get(String path) {
|
||||
public @Nullable ResourceFile get(String path) {
|
||||
return this.files.get(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ import com.thoughtworks.qdox.JavaProjectBuilder;
|
||||
import com.thoughtworks.qdox.model.JavaClass;
|
||||
import com.thoughtworks.qdox.model.JavaSource;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
@@ -20,7 +20,8 @@ import java.util.Iterator;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -117,8 +118,7 @@ public final class SourceFiles implements Iterable<SourceFile> {
|
||||
* @param path the path to find
|
||||
* @return a {@link SourceFile} instance or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public SourceFile get(String path) {
|
||||
public @Nullable SourceFile get(String path) {
|
||||
return this.files.get(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Utility that can be used to dynamically compile and test Java source code.
|
||||
@@ -48,8 +48,7 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public final class TestCompiler {
|
||||
|
||||
@Nullable
|
||||
private final ClassLoader classLoader;
|
||||
private final @Nullable ClassLoader classLoader;
|
||||
|
||||
private final JavaCompiler compiler;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* Support classes for compiling and testing generated code.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
@NullMarked
|
||||
package org.springframework.core.test.tools;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
Reference in New Issue
Block a user