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:
Sébastien Deleuze
2024-12-03 15:22:37 +01:00
parent fcb8aed03f
commit bc5d771a06
3459 changed files with 14118 additions and 22059 deletions

View File

@@ -34,6 +34,7 @@ import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowire;
@@ -49,7 +50,6 @@ import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -33,6 +33,7 @@ import java.util.List;
import java.util.Locale;
import java.util.UUID;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -46,7 +47,6 @@ import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -868,13 +868,11 @@ class BeanUtilsTests {
this.value = value;
}
@Nullable
public Integer getCounter() {
public @Nullable Integer getCounter() {
return counter;
}
@Nullable
public Boolean isFlag() {
public @Nullable Boolean isFlag() {
return flag;
}

View File

@@ -39,6 +39,7 @@ import java.util.stream.IntStream;
import java.util.stream.Stream;
import jakarta.annotation.Priority;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
@@ -87,7 +88,6 @@ import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -40,6 +40,7 @@ import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -73,7 +74,6 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
@@ -4294,8 +4294,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
static class MixedNullableInjectionBean {
@Nullable
public Integer nullableBean;
public @Nullable Integer nullableBean;
public String nonNullBean;
@@ -4309,8 +4308,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
static class MixedOptionalInjectionBean {
@Nullable
public Integer nullableBean;
public @Nullable Integer nullableBean;
public String nonNullBean;

View File

@@ -16,6 +16,7 @@
package org.springframework.beans.factory.aot;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -24,7 +25,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.core.Ordered;
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -205,8 +205,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
private final int order;
@Nullable
private RegisteredBean registeredBean;
private @Nullable RegisteredBean registeredBean;
MockBeanRegistrationExcludeFilter(boolean excluded, int order) {
this.excluded = excluded;

View File

@@ -29,6 +29,7 @@ import java.util.function.Supplier;
import javax.lang.model.element.Modifier;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
@@ -58,7 +59,6 @@ import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
@@ -706,15 +706,13 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
this.name = name;
}
@Nullable
@Override
public String getObject() {
public @Nullable String getObject() {
return getPrefix() + " " + getName();
}
@Nullable
@Override
public Class<?> getObjectType() {
public @Nullable Class<?> getObjectType() {
return String.class;
}

View File

@@ -21,6 +21,7 @@ import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.function.UnaryOperator;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.GenerationContext;
@@ -44,7 +45,6 @@ import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanFactory
import org.springframework.core.ResolvableType;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -22,10 +22,9 @@ import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -147,8 +146,7 @@ class CglibSubclassingInstantiationStrategyTests {
static class MyReplacer implements MethodReplacer {
@Nullable
Object returnValue;
@Nullable Object returnValue;
void reset() {
this.returnValue = null;

View File

@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.FactoryBean;
@@ -32,7 +33,6 @@ import org.springframework.beans.testfixture.beans.factory.generator.factory.Num
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -16,13 +16,13 @@
package org.springframework.beans.factory.xml;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.xml.sax.InputSource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -94,8 +94,7 @@ class ResourceEntityResolverTests {
private final boolean shouldThrow;
@Nullable
private final InputSource returnValue;
private final @Nullable InputSource returnValue;
boolean fallbackInvoked = false;
@@ -112,8 +111,7 @@ class ResourceEntityResolverTests {
}
@Override
@Nullable
protected InputSource resolveSchemaEntity(String publicId, String systemId) {
protected @Nullable InputSource resolveSchemaEntity(String publicId, String systemId) {
this.fallbackInvoked = true;
if (this.shouldThrow) {
throw new ResolutionRejectedException();

View File

@@ -19,10 +19,10 @@ package org.springframework.beans.support;
import java.util.ArrayList;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;