diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml index 04064bd8a80..d869769c17a 100644 --- a/config/checkstyle/checkstyle-suppressions.xml +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -90,4 +90,5 @@ + diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AbstractDependsOnBeanFactoryPostProcessor.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AbstractDependsOnBeanFactoryPostProcessor.java index dacc47b9716..72128981b80 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AbstractDependsOnBeanFactoryPostProcessor.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AbstractDependsOnBeanFactoryPostProcessor.java @@ -22,6 +22,8 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.FactoryBean; @@ -50,7 +52,7 @@ public abstract class AbstractDependsOnBeanFactoryPostProcessor implements BeanF private final Class beanClass; - private final Class> factoryBeanClass; + private final @Nullable Class> factoryBeanClass; private final Function> dependsOn; @@ -61,7 +63,7 @@ public abstract class AbstractDependsOnBeanFactoryPostProcessor implements BeanF * @param dependsOn dependency names */ protected AbstractDependsOnBeanFactoryPostProcessor(Class beanClass, - Class> factoryBeanClass, String... dependsOn) { + @Nullable Class> factoryBeanClass, String... dependsOn) { this.beanClass = beanClass; this.factoryBeanClass = factoryBeanClass; this.dependsOn = (beanFactory) -> new HashSet<>(Arrays.asList(dependsOn)); @@ -75,7 +77,7 @@ public abstract class AbstractDependsOnBeanFactoryPostProcessor implements BeanF * @since 2.1.7 */ protected AbstractDependsOnBeanFactoryPostProcessor(Class beanClass, - Class> factoryBeanClass, Class... dependencyTypes) { + @Nullable Class> factoryBeanClass, Class... dependencyTypes) { this.beanClass = beanClass; this.factoryBeanClass = factoryBeanClass; this.dependsOn = (beanFactory) -> Arrays.stream(dependencyTypes) diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java index 088863aeedd..1b6e149dad8 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java @@ -19,12 +19,15 @@ package org.springframework.boot.autoconfigure; import java.io.IOException; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; +import org.springframework.util.Assert; /** * A {@link TypeFilter} implementation that matches registered auto-configuration classes. @@ -35,9 +38,10 @@ import org.springframework.core.type.filter.TypeFilter; */ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware { + @SuppressWarnings("NullAway.Init") private ClassLoader beanClassLoader; - private volatile List autoConfigurations; + private volatile @Nullable List autoConfigurations; @Override public void setBeanClassLoader(ClassLoader beanClassLoader) { @@ -66,6 +70,7 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad ImportCandidates importCandidates = ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader); this.autoConfigurations = importCandidates.getCandidates(); } + Assert.state(this.autoConfigurations != null, "'autoConfigurations' must not be null"); return this.autoConfigurations; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java index 461dd7deb70..4be32042053 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.context.EnvironmentAware; @@ -54,6 +56,6 @@ public interface AutoConfigurationImportFilter { * {@code autoConfigurationClasses} parameter. Entries containing {@code false} will * not be imported. */ - boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata); + boolean[] match(@Nullable String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java index a50c408d3bb..da3649626e0 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.beans.factory.Aware; @@ -89,23 +90,27 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, private final Class autoConfigurationAnnotation; + @SuppressWarnings("NullAway.Init") private ConfigurableListableBeanFactory beanFactory; + @SuppressWarnings("NullAway.Init") private Environment environment; + @SuppressWarnings("NullAway.Init") private ClassLoader beanClassLoader; + @SuppressWarnings("NullAway.Init") private ResourceLoader resourceLoader; - private volatile ConfigurationClassFilter configurationClassFilter; + private volatile @Nullable ConfigurationClassFilter configurationClassFilter; - private volatile AutoConfigurationReplacements autoConfigurationReplacements; + private volatile @Nullable AutoConfigurationReplacements autoConfigurationReplacements; public AutoConfigurationImportSelector() { this(null); } - AutoConfigurationImportSelector(Class autoConfigurationAnnotation) { + AutoConfigurationImportSelector(@Nullable Class autoConfigurationAnnotation) { this.autoConfigurationAnnotation = (autoConfigurationAnnotation != null) ? autoConfigurationAnnotation : AutoConfiguration.class; } @@ -168,7 +173,7 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, * @param metadata the annotation metadata * @return annotation attributes */ - protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { + protected @Nullable AnnotationAttributes getAttributes(AnnotationMetadata metadata) { String name = getAnnotationClass().getName(); AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); Assert.state(attributes != null, () -> "No auto-configuration attributes found. Is " + metadata.getClassName() @@ -192,7 +197,8 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, * attributes} * @return a list of candidate configurations */ - protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { + protected List getCandidateConfigurations(AnnotationMetadata metadata, + @Nullable AnnotationAttributes attributes) { ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, getBeanClassLoader()); List configurations = importCandidates.getCandidates(); @@ -237,10 +243,12 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, * attributes} * @return exclusions or an empty set */ - protected Set getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { + protected Set getExclusions(AnnotationMetadata metadata, @Nullable AnnotationAttributes attributes) { Set excluded = new LinkedHashSet<>(); - excluded.addAll(asList(attributes, "exclude")); - excluded.addAll(asList(attributes, "excludeName")); + if (attributes != null) { + excluded.addAll(asList(attributes, "exclude")); + excluded.addAll(asList(attributes, "excludeName")); + } excluded.addAll(getExcludeAutoConfigurationsProperty()); return getAutoConfigurationReplacements().replaceAll(excluded); } @@ -389,7 +397,7 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, List filter(List configurations) { long startTime = System.nanoTime(); - String[] candidates = StringUtils.toStringArray(configurations); + @Nullable String[] candidates = StringUtils.toStringArray(configurations); boolean skipped = false; for (AutoConfigurationImportFilter filter : this.filters) { boolean[] match = filter.match(candidates, this.autoConfigurationMetadata); @@ -426,15 +434,18 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, private final List autoConfigurationEntries = new ArrayList<>(); + @SuppressWarnings("NullAway.Init") private ClassLoader beanClassLoader; + @SuppressWarnings("NullAway.Init") private BeanFactory beanFactory; + @SuppressWarnings("NullAway.Init") private ResourceLoader resourceLoader; - private AutoConfigurationMetadata autoConfigurationMetadata; + private @Nullable AutoConfigurationMetadata autoConfigurationMetadata; - private AutoConfigurationReplacements autoConfigurationReplacements; + private @Nullable AutoConfigurationReplacements autoConfigurationReplacements; @Override public void setBeanClassLoader(ClassLoader classLoader) { @@ -488,10 +499,16 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, .collect(Collectors.toCollection(LinkedHashSet::new)); processedConfigurations.removeAll(allExclusions); return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() - .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) + .map(this::getEntry) .toList(); } + private Entry getEntry(String importClassName) { + AnnotationMetadata metadata = this.entries.get(importClassName); + Assert.state(metadata != null, "'metadata' must not be null"); + return new Entry(metadata, importClassName); + } + private AutoConfigurationMetadata getAutoConfigurationMetadata() { if (this.autoConfigurationMetadata == null) { this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); @@ -501,6 +518,8 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, private List sortAutoConfigurations(Set configurations, AutoConfigurationMetadata autoConfigurationMetadata) { + Assert.state(this.autoConfigurationReplacements != null, + "'autoConfigurationReplacements' must not be null"); return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata, this.autoConfigurationReplacements::replace) .getInPriorityOrder(configurations); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadata.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadata.java index 6f094590ef4..5a8d52a8268 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadata.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadata.java @@ -18,6 +18,10 @@ package org.springframework.boot.autoconfigure; import java.util.Set; +import org.jspecify.annotations.Nullable; + +import org.springframework.lang.Contract; + /** * Provides access to meta-data written by the auto-configure annotation processor. * @@ -40,7 +44,7 @@ public interface AutoConfigurationMetadata { * @param key the meta-data key * @return the meta-data value or {@code null} */ - Integer getInteger(String className, String key); + @Nullable Integer getInteger(String className, String key); /** * Get an {@link Integer} value from the meta-data. @@ -49,7 +53,8 @@ public interface AutoConfigurationMetadata { * @param defaultValue the default value * @return the meta-data value or {@code defaultValue} */ - Integer getInteger(String className, String key, Integer defaultValue); + @Contract("_, _, !null -> !null") + @Nullable Integer getInteger(String className, String key, @Nullable Integer defaultValue); /** * Get a {@link Set} value from the meta-data. @@ -57,7 +62,7 @@ public interface AutoConfigurationMetadata { * @param key the meta-data key * @return the meta-data value or {@code null} */ - Set getSet(String className, String key); + @Nullable Set getSet(String className, String key); /** * Get a {@link Set} value from the meta-data. @@ -66,7 +71,8 @@ public interface AutoConfigurationMetadata { * @param defaultValue the default value * @return the meta-data value or {@code defaultValue} */ - Set getSet(String className, String key, Set defaultValue); + @Contract("_, _, !null -> !null") + @Nullable Set getSet(String className, String key, @Nullable Set defaultValue); /** * Get an {@link String} value from the meta-data. @@ -74,7 +80,7 @@ public interface AutoConfigurationMetadata { * @param key the meta-data key * @return the meta-data value or {@code null} */ - String get(String className, String key); + @Nullable String get(String className, String key); /** * Get an {@link String} value from the meta-data. @@ -83,6 +89,7 @@ public interface AutoConfigurationMetadata { * @param defaultValue the default value * @return the meta-data value or {@code defaultValue} */ - String get(String className, String key, String defaultValue); + @Contract("_, _, !null -> !null") + @Nullable String get(String className, String key, @Nullable String defaultValue); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader.java index 60511c07b74..9c1a004f7d4 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader.java @@ -22,6 +22,8 @@ import java.util.Enumeration; import java.util.Properties; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.core.io.UrlResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.util.StringUtils; @@ -42,7 +44,7 @@ final class AutoConfigurationMetadataLoader { return loadMetadata(classLoader, PATH); } - static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { + static AutoConfigurationMetadata loadMetadata(@Nullable ClassLoader classLoader, String path) { try { Enumeration urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); @@ -78,34 +80,34 @@ final class AutoConfigurationMetadataLoader { } @Override - public Integer getInteger(String className, String key) { + public @Nullable Integer getInteger(String className, String key) { return getInteger(className, key, null); } @Override - public Integer getInteger(String className, String key, Integer defaultValue) { + public @Nullable Integer getInteger(String className, String key, @Nullable Integer defaultValue) { String value = get(className, key); return (value != null) ? Integer.valueOf(value) : defaultValue; } @Override - public Set getSet(String className, String key) { + public @Nullable Set getSet(String className, String key) { return getSet(className, key, null); } @Override - public Set getSet(String className, String key, Set defaultValue) { + public @Nullable Set getSet(String className, String key, @Nullable Set defaultValue) { String value = get(className, key); return (value != null) ? StringUtils.commaDelimitedListToSet(value) : defaultValue; } @Override - public String get(String className, String key) { + public @Nullable String get(String className, String key) { return get(className, key, null); } @Override - public String get(String className, String key, String defaultValue) { + public @Nullable String get(String className, String key, @Nullable String defaultValue) { String value = this.properties.getProperty(className + "." + key); return (value != null) ? value : defaultValue; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java index cc4125784b3..70d1ce680c8 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java @@ -30,12 +30,14 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.context.annotation.DeterminableImports; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -105,8 +107,9 @@ public abstract class AutoConfigurationPackages { private static void addBasePackages(BeanDefinition beanDefinition, String[] additionalBasePackages) { ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues(); if (constructorArgumentValues.hasIndexedArgumentValue(0)) { - String[] existingPackages = (String[]) constructorArgumentValues.getIndexedArgumentValue(0, String[].class) - .getValue(); + ValueHolder indexedArgumentValue = constructorArgumentValues.getIndexedArgumentValue(0, String[].class); + Assert.state(indexedArgumentValue != null, "'indexedArgumentValue' must not be null"); + String[] existingPackages = (String[]) indexedArgumentValue.getValue(); constructorArgumentValues.addIndexedArgumentValue(0, Stream.concat(Stream.of(existingPackages), Stream.of(additionalBasePackages)) .distinct() @@ -145,6 +148,7 @@ public abstract class AutoConfigurationPackages { PackageImports(AnnotationMetadata metadata) { AnnotationAttributes attributes = AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false)); + Assert.state(attributes != null, "'attributes' must not be null"); List packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages"))); for (Class basePackageClass : attributes.getClassArray("basePackageClasses")) { packageNames.add(basePackageClass.getPackage().getName()); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java index 661811676f8..043ccb0fee9 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java @@ -28,6 +28,8 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.core.io.UrlResource; import org.springframework.util.Assert; @@ -88,7 +90,7 @@ final class AutoConfigurationReplacements { * @param classLoader class loader to use for loading * @return list of names of annotated classes */ - static AutoConfigurationReplacements load(Class annotation, ClassLoader classLoader) { + static AutoConfigurationReplacements load(Class annotation, @Nullable ClassLoader classLoader) { Assert.notNull(annotation, "'annotation' must not be null"); ClassLoader classLoaderToUse = decideClassloader(classLoader); String location = String.format(LOCATION, annotation.getName()); @@ -101,7 +103,7 @@ final class AutoConfigurationReplacements { return new AutoConfigurationReplacements(replacements); } - private static ClassLoader decideClassloader(ClassLoader classLoader) { + private static ClassLoader decideClassloader(@Nullable ClassLoader classLoader) { if (classLoader == null) { return ImportCandidates.class.getClassLoader(); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java index 812e3cb7d48..8a53a351844 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java @@ -30,6 +30,8 @@ import java.util.Set; import java.util.TreeSet; import java.util.function.UnaryOperator; +import org.jspecify.annotations.Nullable; + import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; @@ -47,12 +49,13 @@ class AutoConfigurationSorter { private final MetadataReaderFactory metadataReaderFactory; - private final AutoConfigurationMetadata autoConfigurationMetadata; + private final @Nullable AutoConfigurationMetadata autoConfigurationMetadata; - private final UnaryOperator replacementMapper; + private final @Nullable UnaryOperator replacementMapper; AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory, - AutoConfigurationMetadata autoConfigurationMetadata, UnaryOperator replacementMapper) { + @Nullable AutoConfigurationMetadata autoConfigurationMetadata, + @Nullable UnaryOperator replacementMapper) { Assert.notNull(metadataReaderFactory, "'metadataReaderFactory' must not be null"); this.metadataReaderFactory = metadataReaderFactory; this.autoConfigurationMetadata = autoConfigurationMetadata; @@ -91,7 +94,7 @@ class AutoConfigurationSorter { } private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List toSort, Set sorted, - Set processing, String current) { + Set processing, @Nullable String current) { if (current == null) { current = toSort.remove(0); } @@ -118,7 +121,7 @@ class AutoConfigurationSorter { private final Map classes = new LinkedHashMap<>(); AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, - AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames) { + @Nullable AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames) { addToClasses(metadataReaderFactory, autoConfigurationMetadata, classNames, true); } @@ -127,7 +130,8 @@ class AutoConfigurationSorter { } private void addToClasses(MetadataReaderFactory metadataReaderFactory, - AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames, boolean required) { + @Nullable AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames, + boolean required) { for (String className : classNames) { if (!this.classes.containsKey(className)) { AutoConfigurationClass autoConfigurationClass = new AutoConfigurationClass(className, @@ -147,7 +151,9 @@ class AutoConfigurationSorter { } AutoConfigurationClass get(String className) { - return this.classes.get(className); + AutoConfigurationClass autoConfigurationClass = this.classes.get(className); + Assert.state(autoConfigurationClass != null, "'autoConfigurationClass' must not be null"); + return autoConfigurationClass; } Set getClassesRequestedAfter(String className) { @@ -168,16 +174,16 @@ class AutoConfigurationSorter { private final MetadataReaderFactory metadataReaderFactory; - private final AutoConfigurationMetadata autoConfigurationMetadata; + private final @Nullable AutoConfigurationMetadata autoConfigurationMetadata; - private volatile AnnotationMetadata annotationMetadata; + private volatile @Nullable AnnotationMetadata annotationMetadata; - private volatile Set before; + private volatile @Nullable Set before; - private volatile Set after; + private volatile @Nullable Set after; AutoConfigurationClass(String className, MetadataReaderFactory metadataReaderFactory, - AutoConfigurationMetadata autoConfigurationMetadata) { + @Nullable AutoConfigurationMetadata autoConfigurationMetadata) { this.className = className; this.metadataReaderFactory = metadataReaderFactory; this.autoConfigurationMetadata = autoConfigurationMetadata; @@ -210,12 +216,15 @@ class AutoConfigurationSorter { } private Set getClassNames(String metadataKey, Class annotation) { - Set annotationValue = wasProcessed() - ? this.autoConfigurationMetadata.getSet(this.className, metadataKey, Collections.emptySet()) - : getAnnotationValue(annotation); + Set annotationValue = wasProcessed() ? getSet(metadataKey) : getAnnotationValue(annotation); return applyReplacements(annotationValue); } + private Set getSet(String metadataKey) { + Assert.state(this.autoConfigurationMetadata != null, "'autoConfigurationMetadata' must not be null"); + return this.autoConfigurationMetadata.getSet(this.className, metadataKey, Collections.emptySet()); + } + private Set applyReplacements(Set values) { if (AutoConfigurationSorter.this.replacementMapper == null) { return values; @@ -229,10 +238,11 @@ class AutoConfigurationSorter { private int getOrder() { if (wasProcessed()) { + Assert.state(this.autoConfigurationMetadata != null, "'autoConfigurationMetadata' must not be null"); return this.autoConfigurationMetadata.getInteger(this.className, "AutoConfigureOrder", AutoConfigureOrder.DEFAULT_ORDER); } - Map attributes = getAnnotationMetadata() + Map attributes = getAnnotationMetadata() .getAnnotationAttributes(AutoConfigureOrder.class.getName()); return (attributes != null) ? (Integer) attributes.get("value") : AutoConfigureOrder.DEFAULT_ORDER; } @@ -243,8 +253,8 @@ class AutoConfigurationSorter { } private Set getAnnotationValue(Class annotation) { - Map attributes = getAnnotationMetadata().getAnnotationAttributes(annotation.getName(), - true); + Map attributes = getAnnotationMetadata() + .getAnnotationAttributes(annotation.getName(), true); if (attributes == null) { return Collections.emptySet(); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java index 5e0ea6a5037..90a74d3da6e 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java @@ -28,6 +28,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.annotation.DeterminableImports; import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -71,12 +73,13 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec } @Override - protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { + protected @Nullable AnnotationAttributes getAttributes(AnnotationMetadata metadata) { return null; } @Override - protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { + protected List getCandidateConfigurations(AnnotationMetadata metadata, + @Nullable AnnotationAttributes attributes) { List candidates = new ArrayList<>(); Map, List> annotations = getAnnotations(metadata); annotations.forEach( @@ -93,13 +96,13 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec private Collection getConfigurationsForAnnotation(Class source, Annotation annotation) { String[] classes = (String[]) AnnotationUtils.getAnnotationAttributes(annotation, true).get("classes"); - if (classes.length > 0) { + if (classes != null && classes.length > 0) { return Arrays.asList(classes); } return loadFactoryNames(source).stream().map(this::mapFactoryName).filter(Objects::nonNull).toList(); } - private String mapFactoryName(String name) { + private @Nullable String mapFactoryName(String name) { if (!name.startsWith(OPTIONAL_PREFIX)) { return name; } @@ -117,7 +120,7 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec } @Override - protected Set getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { + protected Set getExclusions(AnnotationMetadata metadata, @Nullable AnnotationAttributes attributes) { Set exclusions = new LinkedHashSet<>(); Class source = ClassUtils.resolveClassName(metadata.getClassName(), getBeanClassLoader()); for (String annotationName : ANNOTATION_NAMES) { @@ -148,7 +151,7 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec return Collections.unmodifiableMap(annotations); } - private void collectAnnotations(Class source, MultiValueMap, Annotation> annotations, + private void collectAnnotations(@Nullable Class source, MultiValueMap, Annotation> annotations, HashSet> seen) { if (source != null && seen.add(source)) { for (Annotation annotation : source.getDeclaredAnnotations()) { diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java index 9b7bd1a8743..69168d5198f 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java @@ -191,6 +191,7 @@ class SharedMetadataReaderFactoryContextInitializer implements implements FactoryBean, ResourceLoaderAware, ApplicationListener { + @SuppressWarnings("NullAway.Init") private ConcurrentReferenceCachingMetadataReaderFactory metadataReaderFactory; @Override diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/admin/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/admin/package-info.java index 405a872c26d..986580bcb10 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/admin/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/admin/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for admin-related features. */ +@NullMarked package org.springframework.boot.autoconfigure.admin; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/aop/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/aop/package-info.java index 3957906d487..7994bf1ad96 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/aop/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/aop/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for Spring AOP. */ +@NullMarked package org.springframework.boot.autoconfigure.aop; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/package-info.java index c582a49861c..173e0ef2fc5 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/availability/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for application availability features. */ +@NullMarked package org.springframework.boot.autoconfigure.availability; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/package-info.java index 9eaa0a0d7b5..814ca355e61 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration base classes for Caching support. */ +@NullMarked package org.springframework.boot.autoconfigure.cache; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/AbstractNestedCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/AbstractNestedCondition.java index 72260a56630..38e507e2612 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/AbstractNestedCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/AbstractNestedCondition.java @@ -22,6 +22,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; @@ -153,8 +155,8 @@ public abstract class AbstractNestedCondition extends SpringBootCondition implem @SuppressWarnings("unchecked") private List getConditionClasses(AnnotatedTypeMetadata metadata) { - MultiValueMap attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), - true); + MultiValueMap attributes = metadata + .getAllAnnotationAttributes(Conditional.class.getName(), true); Object values = (attributes != null) ? attributes.get("value") : null; return (List) ((values != null) ? values : Collections.emptyList()); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.java index 8e019d0f88f..1a4780c3ec5 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.java @@ -30,6 +30,8 @@ import java.util.TreeMap; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Condition; @@ -58,7 +60,7 @@ public final class ConditionEvaluationReport { private boolean addedAncestorOutcomes; - private ConditionEvaluationReport parent; + private @Nullable ConditionEvaluationReport parent; private final List exclusions = new ArrayList<>(); @@ -154,7 +156,7 @@ public final class ConditionEvaluationReport { * The parent report (from a parent BeanFactory if there is one). * @return the parent report (or null if there isn't one) */ - public ConditionEvaluationReport getParent() { + public @Nullable ConditionEvaluationReport getParent() { return this.parent; } @@ -164,7 +166,7 @@ public final class ConditionEvaluationReport { * @param beanFactory the bean factory (may be {@code null}) * @return the {@link ConditionEvaluationReport} or {@code null} */ - public static ConditionEvaluationReport find(BeanFactory beanFactory) { + public static @Nullable ConditionEvaluationReport find(BeanFactory beanFactory) { if (beanFactory instanceof ConfigurableListableBeanFactory) { return ConditionEvaluationReport.get((ConfigurableListableBeanFactory) beanFactory); } @@ -191,7 +193,7 @@ public final class ConditionEvaluationReport { } } - private static void locateParent(BeanFactory beanFactory, ConditionEvaluationReport report) { + private static void locateParent(@Nullable BeanFactory beanFactory, ConditionEvaluationReport report) { if (beanFactory != null && report.parent == null && beanFactory.containsBean(BEAN_NAME)) { report.parent = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReportAutoConfigurationImportListener.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReportAutoConfigurationImportListener.java index 948b9f7ae0e..6c278172901 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReportAutoConfigurationImportListener.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionEvaluationReportAutoConfigurationImportListener.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.condition; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -32,7 +34,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurationImportListener; class ConditionEvaluationReportAutoConfigurationImportListener implements AutoConfigurationImportListener, BeanFactoryAware { - private ConfigurableListableBeanFactory beanFactory; + private @Nullable ConfigurableListableBeanFactory beanFactory; @Override public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionMessage.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionMessage.java index 2d63a1f7dff..fd04817c106 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionMessage.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionMessage.java @@ -23,6 +23,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -38,13 +40,13 @@ import org.springframework.util.StringUtils; */ public final class ConditionMessage { - private final String message; + private final @Nullable String message; private ConditionMessage() { this(null); } - private ConditionMessage(String message) { + private ConditionMessage(@Nullable String message) { this.message = message; } @@ -87,7 +89,7 @@ public final class ConditionMessage { * @param message the message to append * @return a new {@link ConditionMessage} instance */ - public ConditionMessage append(String message) { + public ConditionMessage append(@Nullable String message) { if (!StringUtils.hasLength(message)) { return this; } @@ -158,7 +160,7 @@ public final class ConditionMessage { * @param messages the source messages (may be {@code null}) * @return a new {@link ConditionMessage} instance */ - public static ConditionMessage of(Collection messages) { + public static ConditionMessage of(@Nullable Collection messages) { ConditionMessage result = new ConditionMessage(); if (messages != null) { for (ConditionMessage message : messages) { @@ -296,7 +298,7 @@ public final class ConditionMessage { * @param reason the reason for the message * @return a built {@link ConditionMessage} */ - public ConditionMessage because(String reason) { + public ConditionMessage because(@Nullable String reason) { if (StringUtils.hasLength(reason)) { return new ConditionMessage(ConditionMessage.this, StringUtils.hasLength(this.condition) ? this.condition + " " + reason : reason); @@ -343,7 +345,7 @@ public final class ConditionMessage { * @param items the items (may be {@code null}) * @return a built {@link ConditionMessage} */ - public ConditionMessage items(Object... items) { + public ConditionMessage items(Object @Nullable ... items) { return items(Style.NORMAL, items); } @@ -355,7 +357,7 @@ public final class ConditionMessage { * @param items the items (may be {@code null}) * @return a built {@link ConditionMessage} */ - public ConditionMessage items(Style style, Object... items) { + public ConditionMessage items(Style style, Object @Nullable ... items) { return items(style, (items != null) ? Arrays.asList(items) : null); } @@ -366,7 +368,7 @@ public final class ConditionMessage { * @param items the source of the items (may be {@code null}) * @return a built {@link ConditionMessage} */ - public ConditionMessage items(Collection items) { + public ConditionMessage items(@Nullable Collection items) { return items(Style.NORMAL, items); } @@ -378,7 +380,7 @@ public final class ConditionMessage { * @param items the source of the items (may be {@code null}) * @return a built {@link ConditionMessage} */ - public ConditionMessage items(Style style, Collection items) { + public ConditionMessage items(Style style, @Nullable Collection items) { Assert.notNull(style, "'style' must not be null"); StringBuilder message = new StringBuilder(this.reason); items = style.applyTo(items); @@ -408,7 +410,7 @@ public final class ConditionMessage { NORMAL { @Override - protected Object applyToItem(Object item) { + protected @Nullable Object applyToItem(@Nullable Object item) { return item; } @@ -420,24 +422,27 @@ public final class ConditionMessage { QUOTE { @Override - protected String applyToItem(Object item) { + protected @Nullable String applyToItem(@Nullable Object item) { return (item != null) ? "'" + item + "'" : null; } }; - public Collection applyTo(Collection items) { + public @Nullable Collection applyTo(@Nullable Collection items) { if (items == null) { return null; } List result = new ArrayList<>(items.size()); for (Object item : items) { - result.add(applyToItem(item)); + Object applied = applyToItem(item); + if (applied != null) { + result.add(applied); + } } return result; } - protected abstract Object applyToItem(Object item); + protected abstract @Nullable Object applyToItem(@Nullable Object item); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java index 0a1ae61c763..ed8d951a5b4 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.condition; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -111,13 +113,13 @@ public class ConditionOutcome { * Return an outcome message or {@code null}. * @return the message or {@code null} */ - public String getMessage() { + public @Nullable String getMessage() { return this.message.isEmpty() ? null : this.message.toString(); } /** - * Return an outcome message or {@code null}. - * @return the message or {@code null} + * Return an outcome message. + * @return the message */ public ConditionMessage getConditionMessage() { return this.message; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.java index 079f24dff78..774b3655747 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.java @@ -21,12 +21,15 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter; import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -39,28 +42,33 @@ import org.springframework.util.CollectionUtils; abstract class FilteringSpringBootCondition extends SpringBootCondition implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware { + @SuppressWarnings("NullAway.Init") private BeanFactory beanFactory; + @SuppressWarnings("NullAway.Init") private ClassLoader beanClassLoader; @Override - public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { + public boolean[] match(@Nullable String[] autoConfigurationClasses, + AutoConfigurationMetadata autoConfigurationMetadata) { ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory); - ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata); + @Nullable ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata); boolean[] match = new boolean[outcomes.length]; for (int i = 0; i < outcomes.length; i++) { match[i] = (outcomes[i] == null || outcomes[i].isMatch()); if (!match[i] && outcomes[i] != null) { - logOutcome(autoConfigurationClasses[i], outcomes[i]); + String autoConfigurationClass = autoConfigurationClasses[i]; + Assert.state(autoConfigurationClass != null, "'autoConfigurationClass' must not be null"); + logOutcome(autoConfigurationClass, outcomes[i]); if (report != null) { - report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]); + report.recordConditionEvaluation(autoConfigurationClass, this, outcomes[i]); } } } return match; } - protected abstract ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + protected abstract @Nullable ConditionOutcome[] getOutcomes(@Nullable String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata); @Override @@ -81,8 +89,8 @@ abstract class FilteringSpringBootCondition extends SpringBootCondition this.beanClassLoader = classLoader; } - protected final List filter(Collection classNames, ClassNameFilter classNameFilter, - ClassLoader classLoader) { + protected final List filter(@Nullable Collection classNames, ClassNameFilter classNameFilter, + @Nullable ClassLoader classLoader) { if (CollectionUtils.isEmpty(classNames)) { return Collections.emptyList(); } @@ -103,7 +111,8 @@ abstract class FilteringSpringBootCondition extends SpringBootCondition * @return a resolved class * @throws ClassNotFoundException if the class cannot be found */ - protected static Class resolve(String className, ClassLoader classLoader) throws ClassNotFoundException { + protected static Class resolve(String className, @Nullable ClassLoader classLoader) + throws ClassNotFoundException { if (classLoader != null) { return Class.forName(className, false, classLoader); } @@ -115,7 +124,7 @@ abstract class FilteringSpringBootCondition extends SpringBootCondition PRESENT { @Override - public boolean matches(String className, ClassLoader classLoader) { + public boolean matches(String className, @Nullable ClassLoader classLoader) { return isPresent(className, classLoader); } @@ -124,15 +133,15 @@ abstract class FilteringSpringBootCondition extends SpringBootCondition MISSING { @Override - public boolean matches(String className, ClassLoader classLoader) { + public boolean matches(String className, @Nullable ClassLoader classLoader) { return !isPresent(className, classLoader); } }; - abstract boolean matches(String className, ClassLoader classLoader); + abstract boolean matches(String className, @Nullable ClassLoader classLoader); - private static boolean isPresent(String className, ClassLoader classLoader) { + private static boolean isPresent(String className, @Nullable ClassLoader classLoader) { if (classLoader == null) { classLoader = ClassUtils.getDefaultClassLoader(); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index 9c436a8e0a6..a548e0c31c3 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -34,6 +34,8 @@ import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; + import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; @@ -60,6 +62,7 @@ import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.MethodMetadata; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -90,9 +93,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } @Override - protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + protected final @Nullable ConditionOutcome[] getOutcomes(@Nullable String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { - ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; + @Nullable ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; for (int i = 0; i < outcomes.length; i++) { String autoConfigurationClass = autoConfigurationClasses[i]; if (autoConfigurationClass != null) { @@ -108,7 +111,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return outcomes; } - private ConditionOutcome getOutcome(Set requiredBeanTypes, Class annotation) { + private @Nullable ConditionOutcome getOutcome(@Nullable Set requiredBeanTypes, + Class annotation) { List missing = filter(requiredBeanTypes, ClassNameFilter.MISSING, getBeanClassLoader()); if (!missing.isEmpty()) { ConditionMessage message = ConditionMessage.forCondition(annotation) @@ -171,7 +175,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return ConditionOutcome .match(spec.message(matchMessage).found("a single bean").items(Style.QUOTE, allBeans)); } - Map beanDefinitions = getBeanDefinitions(spec.context.getBeanFactory(), allBeans, + ConfigurableListableBeanFactory beanFactory = spec.context.getBeanFactory(); + Assert.state(beanFactory != null, "'beanFactory' must not be null"); + Map beanDefinitions = getBeanDefinitions(beanFactory, allBeans, spec.getStrategy() == SearchStrategy.ALL); List primaryBeans = getPrimaryBeans(beanDefinitions); if (primaryBeans.size() == 1) { @@ -249,6 +255,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat private ConfigurableListableBeanFactory getSearchBeanFactory(Spec spec) { ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory(); + Assert.state(beanFactory != null, "'beanFactory' must not be null'"); if (spec.getStrategy() == SearchStrategy.ANCESTORS) { BeanFactory parent = beanFactory.getParentBeanFactory(); Assert.state(parent instanceof ConfigurableListableBeanFactory, @@ -269,8 +276,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return matchedNames; } - private boolean isCandidate(ConfigurableListableBeanFactory beanFactory, String name, BeanDefinition definition, - Set ignoredBeans) { + private boolean isCandidate(ConfigurableListableBeanFactory beanFactory, String name, + @Nullable BeanDefinition definition, Set ignoredBeans) { return (!ignoredBeans.contains(name)) && (definition == null || isAutowireCandidate(beanFactory, name, definition) && isDefaultCandidate(definition)); } @@ -316,12 +323,14 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return (result != null) ? result : Collections.emptyMap(); } - private Map collectBeanDefinitionsForType(ListableBeanFactory beanFactory, + private @Nullable Map collectBeanDefinitionsForType(ListableBeanFactory beanFactory, boolean considerHierarchy, ResolvableType type, Set parameterizedContainers, - Map result) { + @Nullable Map result) { result = putAll(result, beanFactory.getBeanNamesForType(type, true, false), beanFactory); for (ResolvableType parameterizedContainer : parameterizedContainers) { - ResolvableType generic = ResolvableType.forClassWithGenerics(parameterizedContainer.resolve(), type); + Class resolved = parameterizedContainer.resolve(); + Assert.state(resolved != null, "'resolved' must not be null"); + ResolvableType generic = ResolvableType.forClassWithGenerics(resolved, type); result = putAll(result, beanFactory.getBeanNamesForType(generic, true, false), beanFactory); } if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory hierarchicalBeanFactory) { @@ -334,7 +343,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return result; } - private Map getBeanDefinitionsForAnnotation(ClassLoader classLoader, + private Map getBeanDefinitionsForAnnotation(@Nullable ClassLoader classLoader, ConfigurableListableBeanFactory beanFactory, String type, boolean considerHierarchy) throws LinkageError { Map result = null; try { @@ -348,13 +357,14 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } @SuppressWarnings("unchecked") - private Class resolveAnnotationType(ClassLoader classLoader, String type) + private Class resolveAnnotationType(@Nullable ClassLoader classLoader, String type) throws ClassNotFoundException { return (Class) resolve(type, classLoader); } - private Map collectBeanDefinitionsForAnnotation(ListableBeanFactory beanFactory, - Class annotationType, boolean considerHierarchy, Map result) { + private @Nullable Map collectBeanDefinitionsForAnnotation(ListableBeanFactory beanFactory, + Class annotationType, boolean considerHierarchy, + @Nullable Map result) { result = putAll(result, getBeanNamesForAnnotation(beanFactory, annotationType), beanFactory); if (considerHierarchy) { BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory(); @@ -477,7 +487,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return matches; } - private BeanDefinition findBeanDefinition(ConfigurableListableBeanFactory beanFactory, String beanName, + private @Nullable BeanDefinition findBeanDefinition(ConfigurableListableBeanFactory beanFactory, String beanName, boolean considerHierarchy) { if (beanFactory.containsBeanDefinition(beanName)) { return beanFactory.getBeanDefinition(beanName); @@ -489,7 +499,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return null; } - private static Set addAll(Set result, Collection additional) { + private static @Nullable Set addAll(@Nullable Set result, @Nullable Collection additional) { if (CollectionUtils.isEmpty(additional)) { return result; } @@ -498,8 +508,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return result; } - private static Map putAll(Map result, String[] beanNames, - ListableBeanFactory beanFactory) { + private static @Nullable Map putAll(@Nullable Map result, + String[] beanNames, ListableBeanFactory beanFactory) { if (ObjectUtils.isEmpty(beanNames)) { return result; } @@ -517,7 +527,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return result; } - private static BeanDefinition getBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) { + private static @Nullable BeanDefinition getBeanDefinition(String beanName, + ConfigurableListableBeanFactory beanFactory) { try { return beanFactory.getBeanDefinition(beanName); } @@ -548,7 +559,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat private final Set parameterizedContainers; - private final SearchStrategy strategy; + private final @Nullable SearchStrategy strategy; Spec(ConditionContext context, AnnotatedTypeMetadata metadata, MergedAnnotations annotations, Class annotationType) { @@ -621,7 +632,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return resolved; } - protected void validate(BeanTypeDeductionException ex) { + protected void validate(@Nullable BeanTypeDeductionException ex) { if (!hasAtLeastOneElement(getTypes(), getNames(), getAnnotations())) { String message = getAnnotationName() + " did not specify a bean using type, name or annotation"; if (ex == null) { @@ -671,13 +682,13 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return returnType; } - private boolean isParameterizedContainer(Class type) { + private boolean isParameterizedContainer(@Nullable Class type) { return (type != null) && this.parameterizedContainers.stream() .map(ResolvableType::resolve) .anyMatch((container) -> container != null && container.isAssignableFrom(type)); } - private ResolvableType getMethodReturnType(MethodMetadata metadata, ClassLoader classLoader) + private ResolvableType getMethodReturnType(MethodMetadata metadata, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError { Class declaringClass = resolve(metadata.getDeclaringClassName(), classLoader); Method beanMethod = findBeanMethod(declaringClass, metadata.getMethodName()); @@ -698,7 +709,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat throw new IllegalStateException("Unable to find bean method " + methodName); } - private boolean isBeanMethod(Method method) { + @Contract("null -> false") + private boolean isBeanMethod(@Nullable Method method) { return method != null && MergedAnnotations.from(method, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY) .isPresent(Bean.class); } @@ -761,8 +773,10 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat string.append(StringUtils.collectionToCommaDelimitedString(this.ignoredTypes)); string.append("; "); } - string.append("SearchStrategy: "); - string.append(this.strategy.toString().toLowerCase(Locale.ENGLISH)); + if (this.strategy != null) { + string.append("SearchStrategy: "); + string.append(this.strategy.toString().toLowerCase(Locale.ENGLISH)); + } string.append(")"); return string.toString(); } @@ -789,7 +803,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } @Override - protected void validate(BeanTypeDeductionException ex) { + protected void validate(@Nullable BeanTypeDeductionException ex) { Assert.isTrue(getTypes().size() == 1, () -> getAnnotationName() + " annotations must specify only one type (got " + StringUtils.collectionToCommaDelimitedString(getTypes()) + ")"); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java index 852ba74ee5d..7e43351dd87 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter; import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; @@ -44,7 +46,7 @@ import org.springframework.util.StringUtils; class OnClassCondition extends FilteringSpringBootCondition { @Override - protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + protected final @Nullable ConditionOutcome[] getOutcomes(@Nullable String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { // Split the work and perform half in a background thread if more than one // processor is available. Using a single additional thread seems to offer the @@ -59,22 +61,22 @@ class OnClassCondition extends FilteringSpringBootCondition { } } - private ConditionOutcome[] resolveOutcomesThreaded(String[] autoConfigurationClasses, + private @Nullable ConditionOutcome[] resolveOutcomesThreaded(@Nullable String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { int split = autoConfigurationClasses.length / 2; OutcomesResolver firstHalfResolver = createOutcomesResolver(autoConfigurationClasses, 0, split, autoConfigurationMetadata); OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(autoConfigurationClasses, split, autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader()); - ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); - ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); - ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; + @Nullable ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); + @Nullable ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); + @Nullable ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length); System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length); return outcomes; } - private OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses, int start, int end, + private OutcomesResolver createOutcomesResolver(@Nullable String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, start, end, autoConfigurationMetadata, getBeanClassLoader()); @@ -112,8 +114,9 @@ class OnClassCondition extends FilteringSpringBootCondition { return ConditionOutcome.match(matchMessage); } - private List getCandidates(AnnotatedTypeMetadata metadata, Class annotationType) { - MultiValueMap attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true); + private @Nullable List getCandidates(AnnotatedTypeMetadata metadata, Class annotationType) { + MultiValueMap attributes = metadata + .getAllAnnotationAttributes(annotationType.getName(), true); if (attributes == null) { return null; } @@ -123,7 +126,7 @@ class OnClassCondition extends FilteringSpringBootCondition { return candidates; } - private void addAll(List list, List itemsToAdd) { + private void addAll(List list, @Nullable List<@Nullable Object> itemsToAdd) { if (itemsToAdd != null) { for (Object item : itemsToAdd) { Collections.addAll(list, (String[]) item); @@ -133,7 +136,7 @@ class OnClassCondition extends FilteringSpringBootCondition { private interface OutcomesResolver { - ConditionOutcome[] resolveOutcomes(); + @Nullable ConditionOutcome[] resolveOutcomes(); } @@ -141,9 +144,9 @@ class OnClassCondition extends FilteringSpringBootCondition { private final Thread thread; - private volatile ConditionOutcome[] outcomes; + private volatile @Nullable ConditionOutcome @Nullable [] outcomes; - private volatile Throwable failure; + private volatile @Nullable Throwable failure; private ThreadedOutcomesResolver(OutcomesResolver outcomesResolver) { this.thread = new Thread(() -> { @@ -158,7 +161,7 @@ class OnClassCondition extends FilteringSpringBootCondition { } @Override - public ConditionOutcome[] resolveOutcomes() { + public @Nullable ConditionOutcome[] resolveOutcomes() { try { this.thread.join(); } @@ -169,7 +172,7 @@ class OnClassCondition extends FilteringSpringBootCondition { if (failure != null) { ReflectionUtils.rethrowRuntimeException(failure); } - ConditionOutcome[] outcomes = this.outcomes; + @Nullable ConditionOutcome[] outcomes = this.outcomes; return (outcomes != null) ? outcomes : new ConditionOutcome[0]; } @@ -177,7 +180,7 @@ class OnClassCondition extends FilteringSpringBootCondition { private static final class StandardOutcomesResolver implements OutcomesResolver { - private final String[] autoConfigurationClasses; + private final @Nullable String[] autoConfigurationClasses; private final int start; @@ -187,7 +190,7 @@ class OnClassCondition extends FilteringSpringBootCondition { private final ClassLoader beanClassLoader; - private StandardOutcomesResolver(String[] autoConfigurationClasses, int start, int end, + private StandardOutcomesResolver(@Nullable String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata, ClassLoader beanClassLoader) { this.autoConfigurationClasses = autoConfigurationClasses; this.start = start; @@ -197,13 +200,13 @@ class OnClassCondition extends FilteringSpringBootCondition { } @Override - public ConditionOutcome[] resolveOutcomes() { + public @Nullable ConditionOutcome[] resolveOutcomes() { return getOutcomes(this.autoConfigurationClasses, this.start, this.end, this.autoConfigurationMetadata); } - private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end, - AutoConfigurationMetadata autoConfigurationMetadata) { - ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; + private @Nullable ConditionOutcome[] getOutcomes(@Nullable String[] autoConfigurationClasses, int start, + int end, AutoConfigurationMetadata autoConfigurationMetadata) { + @Nullable ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; for (int i = start; i < end; i++) { String autoConfigurationClass = autoConfigurationClasses[i]; if (autoConfigurationClass != null) { @@ -216,7 +219,7 @@ class OnClassCondition extends FilteringSpringBootCondition { return outcomes; } - private ConditionOutcome getOutcome(String candidates) { + private @Nullable ConditionOutcome getOutcome(String candidates) { try { if (!candidates.contains(",")) { return getOutcome(candidates, this.beanClassLoader); @@ -234,7 +237,7 @@ class OnClassCondition extends FilteringSpringBootCondition { return null; } - private ConditionOutcome getOutcome(String className, ClassLoader classLoader) { + private @Nullable ConditionOutcome getOutcome(String className, ClassLoader classLoader) { if (ClassNameFilter.MISSING.matches(className, classLoader)) { return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) .didNotFind("required class") diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnCloudPlatformCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnCloudPlatformCondition.java index ad40d2869ab..3dbfff85799 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnCloudPlatformCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnCloudPlatformCondition.java @@ -18,11 +18,14 @@ package org.springframework.boot.autoconfigure.condition; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.cloud.CloudPlatform; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; /** * {@link Condition} that checks for a required {@link CloudPlatform}. @@ -34,8 +37,11 @@ class OnCloudPlatformCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - Map attributes = metadata.getAnnotationAttributes(ConditionalOnCloudPlatform.class.getName()); + Map attributes = metadata + .getAnnotationAttributes(ConditionalOnCloudPlatform.class.getName()); + Assert.state(attributes != null, "'attributes' must not be null"); CloudPlatform cloudPlatform = (CloudPlatform) attributes.get("value"); + Assert.state(cloudPlatform != null, "'cloudPlatform' must not be null"); return getMatchOutcome(context.getEnvironment(), cloudPlatform); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnExpressionCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnExpressionCondition.java index 2043f9242d7..93ef9d5a4e1 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnExpressionCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnExpressionCondition.java @@ -16,6 +16,10 @@ package org.springframework.boot.autoconfigure.condition; +import java.util.Map; + +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -24,6 +28,7 @@ import org.springframework.context.expression.StandardBeanExpressionResolver; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; /** * A Condition that evaluates a SpEL expression. @@ -37,8 +42,11 @@ class OnExpressionCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - String expression = (String) metadata.getAnnotationAttributes(ConditionalOnExpression.class.getName()) - .get("value"); + Map attributes = metadata + .getAnnotationAttributes(ConditionalOnExpression.class.getName()); + Assert.state(attributes != null, "'attributes' must not be null"); + String expression = (String) attributes.get("value"); + Assert.state(expression != null, "'expression' must not be null"); expression = wrapIfNecessary(expression); ConditionMessage.Builder messageBuilder = ConditionMessage.forCondition(ConditionalOnExpression.class, "(" + expression + ")"); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java index d57c6ef5858..6df16965b67 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.condition; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.Range; import org.springframework.boot.system.JavaVersion; import org.springframework.context.annotation.Condition; @@ -25,6 +27,7 @@ import org.springframework.context.annotation.ConditionContext; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; /** * {@link Condition} that checks for a required version of Java. @@ -40,9 +43,12 @@ class OnJavaCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - Map attributes = metadata.getAnnotationAttributes(ConditionalOnJava.class.getName()); + Map attributes = metadata.getAnnotationAttributes(ConditionalOnJava.class.getName()); + Assert.state(attributes != null, "'attributes' must not be null"); Range range = (Range) attributes.get("range"); + Assert.state(range != null, "'range' must not be null"); JavaVersion version = (JavaVersion) attributes.get("value"); + Assert.state(version != null, "'version' must not be null"); return getMatchOutcome(range, JVM_VERSION, version); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java index cb1e2cc46b6..00dfcdbfa42 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.condition; import javax.naming.NamingException; +import org.jspecify.annotations.Nullable; + import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.Ordered; @@ -26,6 +28,7 @@ import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.jndi.JndiLocatorSupport; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -39,9 +42,10 @@ class OnJndiCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - AnnotationAttributes annotationAttributes = AnnotationAttributes + AnnotationAttributes attributes = AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(ConditionalOnJndi.class.getName())); - String[] locations = annotationAttributes.getStringArray("value"); + Assert.state(attributes != null, "'attributes' must not be null"); + String[] locations = attributes.getStringArray("value"); try { return getMatchOutcome(locations); } @@ -88,7 +92,7 @@ class OnJndiCondition extends SpringBootCondition { this.locations = locations; } - public String lookupFirstLocation() { + public @Nullable String lookupFirstLocation() { for (String location : this.locations) { try { lookup(location); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java index 1043f4c6fca..0d965bb09ec 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java @@ -22,6 +22,8 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; @@ -129,10 +131,16 @@ class OnPropertyCondition extends SpringBootCondition { this.annotationType = annotationType; this.prefix = (!annotationAttributes.containsKey("prefix")) ? "" : getPrefix(annotationAttributes); this.names = getNames(annotationAttributes); - this.havingValue = annotationAttributes.get("havingValue").toString(); + this.havingValue = getHavingValue(annotationAttributes); this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing"); } + private static String getHavingValue(AnnotationAttributes annotationAttributes) { + Object havingValue = annotationAttributes.get("havingValue"); + Assert.state(havingValue != null, "'havingValue' must not be null"); + return havingValue.toString(); + } + private String getPrefix(AnnotationAttributes annotationAttributes) { String prefix = annotationAttributes.getString("prefix").trim(); if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) { @@ -144,6 +152,8 @@ class OnPropertyCondition extends SpringBootCondition { private String[] getNames(AnnotationAttributes annotationAttributes) { String[] value = (String[]) annotationAttributes.get("value"); String[] name = (String[]) annotationAttributes.get("name"); + Assert.state(value != null, "'value' must not be null"); + Assert.state(name != null, "'name' must not be null"); Assert.state(value.length > 0 || name.length > 0, () -> "The name or value attribute of @%s must be specified" .formatted(ClassUtils.getShortName(this.annotationType))); @@ -169,7 +179,7 @@ class OnPropertyCondition extends SpringBootCondition { } } - private boolean isMatch(String value, String requiredValue) { + private boolean isMatch(@Nullable String value, String requiredValue) { if (StringUtils.hasLength(requiredValue)) { return requiredValue.equalsIgnoreCase(value); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnResourceCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnResourceCondition.java index 6b004fc4cdf..03e2cb030da 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnResourceCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnResourceCondition.java @@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.condition; import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; @@ -40,11 +42,14 @@ class OnResourceCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - MultiValueMap attributes = metadata + MultiValueMap attributes = metadata .getAllAnnotationAttributes(ConditionalOnResource.class.getName(), true); + Assert.state(attributes != null, "'attributes' must not be null"); ResourceLoader loader = context.getResourceLoader(); List locations = new ArrayList<>(); - collectValues(locations, attributes.get("resources")); + List<@Nullable Object> resources = attributes.get("resources"); + Assert.state(resources != null, "'resources' must not be null"); + collectValues(locations, resources); Assert.state(!locations.isEmpty(), "@ConditionalOnResource annotations must specify at least one resource location"); List missing = new ArrayList<>(); @@ -64,10 +69,13 @@ class OnResourceCondition extends SpringBootCondition { .items(locations)); } - private void collectValues(List names, List values) { - for (Object value : values) { - for (Object item : (Object[]) value) { - names.add((String) item); + private void collectValues(List names, List<@Nullable Object> resources) { + for (Object resource : resources) { + Object[] items = (Object[]) resource; + if (items != null) { + for (Object item : items) { + names.add((String) item); + } } } } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnThreadingCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnThreadingCondition.java index 34fec9d448b..e349cd55ba5 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnThreadingCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnThreadingCondition.java @@ -18,11 +18,14 @@ package org.springframework.boot.autoconfigure.condition; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.thread.Threading; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; /** * {@link Condition} that checks for a required {@link Threading}. @@ -34,8 +37,11 @@ class OnThreadingCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - Map attributes = metadata.getAnnotationAttributes(ConditionalOnThreading.class.getName()); + Map attributes = metadata + .getAnnotationAttributes(ConditionalOnThreading.class.getName()); + Assert.state(attributes != null, "'attributes' must not be null"); Threading threading = (Threading) attributes.get("value"); + Assert.state(threading != null, "'threading' must not be null"); return getMatchOutcome(context.getEnvironment(), threading); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnWebApplicationCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnWebApplicationCondition.java index 912aa4aeaf7..05cc67c2698 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnWebApplicationCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnWebApplicationCondition.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.condition; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.web.context.reactive.ConfigurableReactiveWebEnvironment; @@ -27,6 +29,7 @@ import org.springframework.context.annotation.ConditionContext; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.web.context.ConfigurableWebEnvironment; @@ -49,9 +52,9 @@ class OnWebApplicationCondition extends FilteringSpringBootCondition { private static final String REACTIVE_WEB_APPLICATION_CLASS = "org.springframework.web.reactive.HandlerResult"; @Override - protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, + protected @Nullable ConditionOutcome[] getOutcomes(@Nullable String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { - ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; + @Nullable ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; for (int i = 0; i < outcomes.length; i++) { String autoConfigurationClass = autoConfigurationClasses[i]; if (autoConfigurationClass != null) { @@ -62,7 +65,7 @@ class OnWebApplicationCondition extends FilteringSpringBootCondition { return outcomes; } - private ConditionOutcome getOutcome(String type) { + private @Nullable ConditionOutcome getOutcome(@Nullable String type) { if (type == null) { return null; } @@ -157,9 +160,12 @@ class OnWebApplicationCondition extends FilteringSpringBootCondition { } private Type deduceType(AnnotatedTypeMetadata metadata) { - Map attributes = metadata.getAnnotationAttributes(ConditionalOnWebApplication.class.getName()); + Map attributes = metadata + .getAnnotationAttributes(ConditionalOnWebApplication.class.getName()); if (attributes != null) { - return (Type) attributes.get("type"); + Object type = attributes.get("type"); + Assert.state(type != null, "'type' must not be null"); + return (Type) type; } return Type.ANY; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/package-info.java index 2916c1ce695..518c0a59175 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/package-info.java @@ -17,4 +17,7 @@ /** * {@code @Condition} annotations and supporting classes. */ +@NullMarked package org.springframework.boot.autoconfigure.condition; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java index aaa6d120c9d..11e6a016004 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.container; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.AttributeAccessor; @@ -29,7 +31,7 @@ import org.springframework.core.AttributeAccessor; * @author Phillip Webb * @since 3.4.0 */ -public record ContainerImageMetadata(String imageName) { +public record ContainerImageMetadata(@Nullable String imageName) { static final String NAME = ContainerImageMetadata.class.getName(); @@ -37,7 +39,7 @@ public record ContainerImageMetadata(String imageName) { * Add this container image metadata to the given attributes. * @param attributes the attributes to add the metadata to */ - public void addTo(AttributeAccessor attributes) { + public void addTo(@Nullable AttributeAccessor attributes) { if (attributes != null) { attributes.setAttribute(NAME, this); } @@ -49,7 +51,7 @@ public record ContainerImageMetadata(String imageName) { * @param attributes the attributes to check * @return if metadata is present */ - public static boolean isPresent(AttributeAccessor attributes) { + public static boolean isPresent(@Nullable AttributeAccessor attributes) { return getFrom(attributes) != null; } @@ -59,7 +61,7 @@ public record ContainerImageMetadata(String imageName) { * @param attributes the attributes * @return the metadata or {@code null} */ - public static ContainerImageMetadata getFrom(AttributeAccessor attributes) { + public static @Nullable ContainerImageMetadata getFrom(@Nullable AttributeAccessor attributes) { return (attributes != null) ? (ContainerImageMetadata) attributes.getAttribute(NAME) : null; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java index c7f82d53702..f9296c21e53 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java @@ -17,4 +17,7 @@ /** * Support classes related to auto-configuration involving containers. */ +@NullMarked package org.springframework.boot.autoconfigure.container; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index fa2e7ad661d..942dffed95c 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -22,6 +22,8 @@ import java.time.Duration; import java.util.List; import java.util.Properties; +import org.jspecify.annotations.Nullable; + import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -92,7 +94,7 @@ public final class MessageSourceAutoConfiguration { return messageSource; } - private Properties loadCommonMessages(List resources) { + private @Nullable Properties loadCommonMessages(@Nullable List resources) { if (CollectionUtils.isEmpty(resources)) { return null; } @@ -135,7 +137,7 @@ public final class MessageSourceAutoConfiguration { return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll()); } - private Resource[] getResources(ClassLoader classLoader, String name) { + private Resource[] getResources(@Nullable ClassLoader classLoader, String name) { String target = name.replace('.', '/'); try { return new PathMatchingResourcePatternResolver(classLoader) @@ -151,7 +153,7 @@ public final class MessageSourceAutoConfiguration { static class MessageSourceRuntimeHints implements RuntimeHintsRegistrar { @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { hints.resources().registerPattern("messages.properties").registerPattern("messages_*.properties"); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java index 06e8bf95aca..1bf0f27275b 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java @@ -23,6 +23,8 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.convert.DurationUnit; import org.springframework.core.io.Resource; @@ -49,7 +51,7 @@ public class MessageSourceProperties { /** * List of locale-independent property file resources containing common messages. */ - private List commonMessages; + private @Nullable List commonMessages; /** * Message bundles encoding. @@ -61,7 +63,7 @@ public class MessageSourceProperties { * forever. If a duration suffix is not specified, seconds will be used. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration cacheDuration; + private @Nullable Duration cacheDuration; /** * Whether to fall back to the system Locale if no files for a specific Locale have @@ -98,11 +100,11 @@ public class MessageSourceProperties { this.encoding = encoding; } - public Duration getCacheDuration() { + public @Nullable Duration getCacheDuration() { return this.cacheDuration; } - public void setCacheDuration(Duration cacheDuration) { + public void setCacheDuration(@Nullable Duration cacheDuration) { this.cacheDuration = cacheDuration; } @@ -130,11 +132,11 @@ public class MessageSourceProperties { this.useCodeAsDefaultMessage = useCodeAsDefaultMessage; } - public List getCommonMessages() { + public @Nullable List getCommonMessages() { return this.commonMessages; } - public void setCommonMessages(List commonMessages) { + public void setCommonMessages(@Nullable List commonMessages) { this.commonMessages = commonMessages; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/package-info.java index 11b948d5543..a4fc610981f 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for the Spring context. */ +@NullMarked package org.springframework.boot.autoconfigure.context; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java index f4c47897ecf..6be8d68fed6 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.data; import java.lang.annotation.Annotation; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -48,15 +50,18 @@ import org.springframework.data.util.Streamable; public abstract class AbstractRepositoryConfigurationSourceSupport implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware { + @SuppressWarnings("NullAway.Init") private ResourceLoader resourceLoader; + @SuppressWarnings("NullAway.Init") private BeanFactory beanFactory; + @SuppressWarnings("NullAway.Init") private Environment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, - BeanNameGenerator importBeanNameGenerator) { + @Nullable BeanNameGenerator importBeanNameGenerator) { RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate( getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment); delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension()); @@ -68,7 +73,7 @@ public abstract class AbstractRepositoryConfigurationSourceSupport } private AnnotationRepositoryConfigurationSource getConfigurationSource(BeanDefinitionRegistry registry, - BeanNameGenerator importBeanNameGenerator) { + @Nullable BeanNameGenerator importBeanNameGenerator) { AnnotationMetadata metadata = AnnotationMetadata.introspect(getConfiguration()); return new AutoConfiguredAnnotationRepositoryConfigurationSource(metadata, getAnnotation(), this.resourceLoader, this.environment, registry, importBeanNameGenerator) { @@ -129,7 +134,7 @@ public abstract class AbstractRepositoryConfigurationSourceSupport AutoConfiguredAnnotationRepositoryConfigurationSource(AnnotationMetadata metadata, Class annotation, ResourceLoader resourceLoader, Environment environment, - BeanDefinitionRegistry registry, BeanNameGenerator generator) { + BeanDefinitionRegistry registry, @Nullable BeanNameGenerator generator) { super(metadata, annotation, resourceLoader, environment, registry, generator); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java index f47f474b3dd..a4de7747810 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/OnRepositoryTypeCondition.java @@ -19,12 +19,15 @@ package org.springframework.boot.autoconfigure.data; import java.util.Locale; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.Assert; /** * {@link SpringBootCondition} for controlling what type of Spring Data repositories are @@ -36,10 +39,14 @@ class OnRepositoryTypeCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - Map attributes = metadata.getAnnotationAttributes(ConditionalOnRepositoryType.class.getName(), - true); - RepositoryType configuredType = getTypeProperty(context.getEnvironment(), (String) attributes.get("store")); + Map attributes = metadata + .getAnnotationAttributes(ConditionalOnRepositoryType.class.getName(), true); + Assert.state(attributes != null, "'attributes' must not be null"); + String store = (String) attributes.get("store"); + Assert.state(store != null, "'store' must not be null"); + RepositoryType configuredType = getTypeProperty(context.getEnvironment(), store); RepositoryType requiredType = (RepositoryType) attributes.get("type"); + Assert.state(requiredType != null, "'requiredType' must not be null"); ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnRepositoryType.class); if (configuredType == requiredType || configuredType == RepositoryType.AUTO) { return ConditionOutcome diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/package-info.java index 4d1dc784a52..b99076c3175 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration base classes for Spring Data. */ +@NullMarked package org.springframework.boot.autoconfigure.data; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java index dd17c090411..634e2d11faa 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InjectionPoint; @@ -74,7 +76,8 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz } @Override - protected FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause, String description) { + protected @Nullable FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause, + @Nullable String description) { if (cause.getNumberOfBeansFound() != 0) { return null; } @@ -118,7 +121,9 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz } private Class extractBeanType(ResolvableType resolvableType) { - return resolvableType.getRawClass(); + Class rawClass = resolvableType.getRawClass(); + Assert.state(rawClass != null, "'rawClass' must not be null"); + return rawClass; } private List getAutoConfigurationResults(NoSuchBeanDefinitionException cause) { @@ -140,7 +145,7 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz .toList(); } - private MethodMetadata getFactoryMethodMetadata(String beanName) { + private @Nullable MethodMetadata getFactoryMethodMetadata(String beanName) { BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(beanName); if (beanDefinition instanceof AnnotatedBeanDefinition annotatedBeanDefinition) { return annotatedBeanDefinition.getFactoryMethodMetadata(); @@ -183,7 +188,7 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz } } - private InjectionPoint findInjectionPoint(Throwable failure) { + private @Nullable InjectionPoint findInjectionPoint(Throwable failure) { UnsatisfiedDependencyException unsatisfiedDependencyException = findCause(failure, UnsatisfiedDependencyException.class); if (unsatisfiedDependencyException == null) { @@ -196,7 +201,7 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz private final String className; - private final String methodName; + private final @Nullable String methodName; Source(String source) { String[] tokens = source.split("#"); @@ -208,7 +213,7 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz return this.className; } - String getMethodName() { + @Nullable String getMethodName() { return this.methodName; } @@ -252,7 +257,7 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz } private boolean hasName(MethodMetadata methodMetadata, String name) { - Map attributes = methodMetadata.getAnnotationAttributes(Bean.class.getName()); + Map attributes = methodMetadata.getAnnotationAttributes(Bean.class.getName()); String[] candidates = (attributes != null) ? (String[]) attributes.get("name") : null; if (candidates != null) { for (String candidate : candidates) { @@ -309,11 +314,11 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz private static class UserConfigurationResult { - private final MethodMetadata methodMetadata; + private final @Nullable MethodMetadata methodMetadata; private final boolean nullBean; - UserConfigurationResult(MethodMetadata methodMetadata, boolean nullBean) { + UserConfigurationResult(@Nullable MethodMetadata methodMetadata, boolean nullBean) { this.methodMetadata = methodMetadata; this.nullBean = nullBean; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/package-info.java index c21aacdfd70..a26756c1e84 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/package-info.java @@ -18,4 +18,7 @@ * Internal {@link org.springframework.boot.diagnostics.FailureAnalyzer} implementations * related to auto-configuration. */ +@NullMarked package org.springframework.boot.autoconfigure.diagnostics.analyzer; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java index 8988ace9036..bae43215b24 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java @@ -139,6 +139,7 @@ public class EntityScanPackages { private Set getPackagesToScan(AnnotationMetadata metadata) { AnnotationAttributes attributes = AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(EntityScan.class.getName())); + Assert.state(attributes != null, "'attributes' must not be null"); Set packagesToScan = new LinkedHashSet<>(); for (String basePackage : attributes.getStringArray("basePackages")) { String[] tokenized = StringUtils.tokenizeToStringArray( diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java index c9f30335004..85c66738446 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java @@ -72,7 +72,9 @@ public class EntityScanner { for (String basePackage : packages) { if (StringUtils.hasText(basePackage)) { for (BeanDefinition candidate : scanner.findCandidateComponents(basePackage)) { - entitySet.add(ClassUtils.forName(candidate.getBeanClassName(), this.context.getClassLoader())); + String beanClassName = candidate.getBeanClassName(); + Assert.state(beanClassName != null, "'beanClassName' must not be null"); + entitySet.add(ClassUtils.forName(beanClassName, this.context.getClassLoader())); } } } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java index 88bedc7c446..033f017bb33 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java @@ -17,4 +17,7 @@ /** * General purpose domain annotations and classes. */ +@NullMarked package org.springframework.boot.autoconfigure.domain; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java index ce19cdca9e2..f08b08a211c 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java @@ -20,6 +20,8 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.Properties; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionMessage; @@ -85,7 +87,7 @@ public final class ProjectInfoAutoConfiguration { return target; } - private Properties loadSource(Resource location, Charset encoding) throws IOException { + private Properties loadSource(Resource location, @Nullable Charset encoding) throws IOException { if (encoding != null) { return PropertiesLoaderUtils.loadProperties(new EncodedResource(location, encoding)); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/package-info.java index 35a01c275a2..ffdb5de60f3 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for project information. */ +@NullMarked package org.springframework.boot.autoconfigure.info; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java index c1139ef0e1e..941a7ca1339 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java @@ -34,6 +34,7 @@ import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource; import org.springframework.jmx.export.annotation.AnnotationMBeanExporter; import org.springframework.jmx.export.naming.ObjectNamingStrategy; import org.springframework.jmx.support.MBeanServerFactoryBean; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -94,7 +95,9 @@ public final class JmxAutoConfiguration { MBeanServerFactoryBean factory = new MBeanServerFactoryBean(); factory.setLocateExistingServerIfPossible(true); factory.afterPropertiesSet(); - return factory.getObject(); + MBeanServer mBeanServer = factory.getObject(); + Assert.state(mBeanServer != null, "'mBeanServer' must not be null"); + return mBeanServer; } } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java index 8bc5b6ac965..973823b32cf 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.jmx; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.jmx.support.RegistrationPolicy; @@ -46,7 +48,7 @@ public class JmxProperties { /** * JMX domain name. */ - private String defaultDomain; + private @Nullable String defaultDomain; /** * JMX Registration policy. @@ -77,11 +79,11 @@ public class JmxProperties { this.server = server; } - public String getDefaultDomain() { + public @Nullable String getDefaultDomain() { return this.defaultDomain; } - public void setDefaultDomain(String defaultDomain) { + public void setDefaultDomain(@Nullable String defaultDomain) { this.defaultDomain = defaultDomain; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/ParentAwareNamingStrategy.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/ParentAwareNamingStrategy.java index bb9eab2fc15..4b4ebb115f7 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/ParentAwareNamingStrategy.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/ParentAwareNamingStrategy.java @@ -21,6 +21,8 @@ import java.util.Hashtable; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -28,6 +30,7 @@ import org.springframework.jmx.export.metadata.JmxAttributeSource; import org.springframework.jmx.export.naming.MetadataNamingStrategy; import org.springframework.jmx.support.JmxUtils; import org.springframework.jmx.support.ObjectNameManager; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** @@ -39,6 +42,7 @@ import org.springframework.util.ObjectUtils; */ public class ParentAwareNamingStrategy extends MetadataNamingStrategy implements ApplicationContextAware { + @SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext; private boolean ensureUniqueRuntimeObjectNames; @@ -62,7 +66,7 @@ public class ParentAwareNamingStrategy extends MetadataNamingStrategy implements } @Override - public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException { + public ObjectName getObjectName(Object managedBean, @Nullable String beanKey) throws MalformedObjectNameException { ObjectName name = super.getObjectName(managedBean, beanKey); if (this.ensureUniqueRuntimeObjectNames) { return JmxUtils.appendIdentityToObjectName(name, managedBean); @@ -73,12 +77,15 @@ public class ParentAwareNamingStrategy extends MetadataNamingStrategy implements return name; } - private boolean parentContextContainsSameBean(ApplicationContext context, String beanKey) { + private boolean parentContextContainsSameBean(ApplicationContext context, @Nullable String beanKey) { if (context.getParent() == null) { return false; } try { - this.applicationContext.getParent().getBean(beanKey); + ApplicationContext parent = this.applicationContext.getParent(); + Assert.state(parent != null, "'parent' must not be null"); + Assert.state(beanKey != null, "'beanKey' must not be null"); + parent.getBean(beanKey); return true; } catch (BeansException ex) { diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/package-info.java index b43007275c9..8dd6b4dde2b 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for JMX. */ +@NullMarked package org.springframework.boot.autoconfigure.jmx; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java index 10e50555519..b7ecb4303b1 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.logging; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.logging.LogLevel; @@ -124,7 +126,7 @@ public class ConditionEvaluationReportLoggingListener } @Override - public boolean supportsSourceType(Class sourceType) { + public boolean supportsSourceType(@Nullable Class sourceType) { return true; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java index c38e839d4a6..37436530970 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.logging; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -31,7 +33,8 @@ import org.springframework.boot.logging.LogLevel; class ConditionEvaluationReportLoggingProcessor implements BeanFactoryInitializationAotProcessor { @Override - public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { + public @Nullable BeanFactoryInitializationAotContribution processAheadOfTime( + ConfigurableListableBeanFactory beanFactory) { logConditionEvaluationReport(beanFactory); return null; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportMessage.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportMessage.java index 3192fa1bfee..8a3e906be09 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportMessage.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportMessage.java @@ -27,6 +27,7 @@ import java.util.Set; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -133,6 +134,7 @@ public class ConditionEvaluationReportMessage { Map result = new LinkedHashMap<>(); for (String shortName : shortNames) { List fullyQualifiedNames = map.get(shortName); + Assert.state(fullyQualifiedNames != null, "'fullyQualifiedNames' must not be null"); if (fullyQualifiedNames.size() > 1) { fullyQualifiedNames .forEach((fullyQualifiedName) -> result.put(fullyQualifiedName, outcomes.get(fullyQualifiedName))); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/package-info.java index 8c0c6e79d5d..5ac723d7f03 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for logging. */ +@NullMarked package org.springframework.boot.autoconfigure.logging; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/package-info.java index dabf6c72dc0..5d1b09dde6c 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/package-info.java @@ -19,4 +19,7 @@ * * @see org.springframework.boot.autoconfigure.EnableAutoConfiguration */ +@NullMarked package org.springframework.boot.autoconfigure; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/preinitialize/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/preinitialize/package-info.java index e7355136f18..2abdc99b464 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/preinitialize/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/preinitialize/package-info.java @@ -17,4 +17,7 @@ /** * Capabilities to preinitialize code in the background to improve startup performance. */ +@NullMarked package org.springframework.boot.autoconfigure.preinitialize; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactories.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactories.java index 6e49280ccbd..e74d3e71052 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactories.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactories.java @@ -26,6 +26,7 @@ import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; @@ -53,7 +54,7 @@ public class ConnectionDetailsFactories { * @param classLoader the class loader used to load factories * @since 3.5.0 */ - public ConnectionDetailsFactories(ClassLoader classLoader) { + public ConnectionDetailsFactories(@Nullable ClassLoader classLoader) { this(SpringFactoriesLoader.forDefaultResourceLocation(classLoader)); } @@ -126,9 +127,10 @@ public class ConnectionDetailsFactories { ConnectionDetailsFactory factory) { @SuppressWarnings("unchecked") - private static Registration get(ConnectionDetailsFactory factory) { + private static @Nullable Registration get( + ConnectionDetailsFactory factory) { ResolvableType type = ResolvableType.forClass(ConnectionDetailsFactory.class, factory.getClass()); - Class[] generics = type.resolveGenerics(); + @Nullable Class[] generics = type.resolveGenerics(); Class sourceType = (Class) generics[0]; Class connectionDetailsType = (Class) generics[1]; return (sourceType != null && connectionDetailsType != null) diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactory.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactory.java index 2c66ae1232a..cfc5aa7b12a 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactory.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactory.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.service.connection; +import org.jspecify.annotations.Nullable; + /** * A factory to create {@link ConnectionDetails} from a given {@code source}. * Implementations should be registered in {@code META-INF/spring.factories}. @@ -36,6 +38,6 @@ public interface ConnectionDetailsFactory { * @param source the source * @return the connection details or {@code null} */ - D getConnectionDetails(S source); + @Nullable D getConnectionDetails(S source); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/package-info.java index 3c3ab6b900e..168158e24b0 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/package-info.java @@ -17,4 +17,7 @@ /** * Support for service connections that affect auto-configuration. */ +@NullMarked package org.springframework.boot.autoconfigure.service.connection; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/BundleContentProperty.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/BundleContentProperty.java index ff63d43e03d..e8a9798f9af 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/BundleContentProperty.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/BundleContentProperty.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.ssl; import java.nio.file.Path; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.ssl.pem.PemContent; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -33,7 +35,7 @@ import org.springframework.util.StringUtils; * @author Phillip Webb * @author Moritz Halbritter */ -record BundleContentProperty(String name, String value) { +record BundleContentProperty(String name, @Nullable String value) { /** * Return if the property value is PEM content. @@ -54,6 +56,7 @@ record BundleContentProperty(String name, String value) { Path toWatchPath(ResourceLoader resourceLoader) { try { Assert.state(!isPemContent(), "Value contains PEM content"); + Assert.state(this.value != null, "Value must not be null"); Resource resource = resourceLoader.getResource(this.value); if (!resource.isFile()) { throw new BundleContentNotWatchableException(this); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/CertificateMatcher.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/CertificateMatcher.java index d0d38daab51..8b3e0ac5789 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/CertificateMatcher.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/CertificateMatcher.java @@ -26,6 +26,8 @@ import java.security.cert.Certificate; import java.util.List; import java.util.Objects; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -47,17 +49,18 @@ class CertificateMatcher { private final Signature signature; - private final byte[] generatedSignature; + private final byte @Nullable [] generatedSignature; CertificateMatcher(PrivateKey privateKey) { Assert.notNull(privateKey, "'privateKey' must not be null"); this.privateKey = privateKey; - this.signature = createSignature(privateKey); - Assert.state(this.signature != null, "Failed to create signature"); - this.generatedSignature = sign(this.signature, privateKey); + Signature signature = createSignature(privateKey); + Assert.state(signature != null, "Failed to create signature"); + this.signature = signature; + this.generatedSignature = sign(signature, privateKey); } - private Signature createSignature(PrivateKey privateKey) { + private @Nullable Signature createSignature(PrivateKey privateKey) { try { String algorithm = getSignatureAlgorithm(privateKey); return (algorithm != null) ? Signature.getInstance(algorithm) : null; @@ -67,7 +70,7 @@ class CertificateMatcher { } } - private static String getSignatureAlgorithm(PrivateKey privateKey) { + private static @Nullable String getSignatureAlgorithm(PrivateKey privateKey) { // https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#signature-algorithms // https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#keypairgenerator-algorithms return switch (privateKey.getAlgorithm()) { @@ -103,7 +106,7 @@ class CertificateMatcher { } } - private static byte[] sign(Signature signature, PrivateKey privateKey) { + private static byte @Nullable [] sign(Signature signature, PrivateKey privateKey) { try { signature.initSign(privateKey); signature.update(DATA); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/FileWatcher.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/FileWatcher.java index 7963f70910b..7b6218fbaaf 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/FileWatcher.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/FileWatcher.java @@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; @@ -57,7 +58,7 @@ class FileWatcher implements Closeable { private final Object lock = new Object(); - private WatcherThread thread; + private @Nullable WatcherThread thread; /** * Create a new {@link FileWatcher} instance. @@ -241,6 +242,7 @@ class FileWatcher implements Closeable { Path directory = (Path) key.watchable(); for (WatchEvent event : key.pollEvents()) { Path file = directory.resolve((Path) event.context()); + Assert.state(registrations != null, "'registrations' must not be null"); for (Registration registration : registrations) { if (registration.manages(file)) { actions.add(registration.action()); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java index 0d43ce5f7eb..7278d05804a 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.ssl; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.ssl.jks.JksSslStoreBundle; /** @@ -54,52 +56,52 @@ public class JksSslBundleProperties extends SslBundleProperties { /** * Type of the store to create, e.g. JKS. */ - private String type; + private @Nullable String type; /** * Provider for the store. */ - private String provider; + private @Nullable String provider; /** * Location of the resource containing the store content. */ - private String location; + private @Nullable String location; /** * Password used to access the store. */ - private String password; + private @Nullable String password; - public String getType() { + public @Nullable String getType() { return this.type; } - public void setType(String type) { + public void setType(@Nullable String type) { this.type = type; } - public String getProvider() { + public @Nullable String getProvider() { return this.provider; } - public void setProvider(String provider) { + public void setProvider(@Nullable String provider) { this.provider = provider; } - public String getLocation() { + public @Nullable String getLocation() { return this.location; } - public void setLocation(String location) { + public void setLocation(@Nullable String location) { this.location = location; } - public String getPassword() { + public @Nullable String getPassword() { return this.password; } - public void setPassword(String password) { + public void setPassword(@Nullable String password) { this.password = password; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java index e47f0de1578..f411e291680 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.ssl; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.ssl.pem.PemSslStoreBundle; /** @@ -55,57 +57,57 @@ public class PemSslBundleProperties extends SslBundleProperties { /** * Type of the store to create, e.g. JKS. */ - private String type; + private @Nullable String type; /** * Location or content of the certificate or certificate chain in PEM format. */ - private String certificate; + private @Nullable String certificate; /** * Location or content of the private key in PEM format. */ - private String privateKey; + private @Nullable String privateKey; /** * Password used to decrypt an encrypted private key. */ - private String privateKeyPassword; + private @Nullable String privateKeyPassword; /** * Whether to verify that the private key matches the public key. */ private boolean verifyKeys; - public String getType() { + public @Nullable String getType() { return this.type; } - public void setType(String type) { + public void setType(@Nullable String type) { this.type = type; } - public String getCertificate() { + public @Nullable String getCertificate() { return this.certificate; } - public void setCertificate(String certificate) { + public void setCertificate(@Nullable String certificate) { this.certificate = certificate; } - public String getPrivateKey() { + public @Nullable String getPrivateKey() { return this.privateKey; } - public void setPrivateKey(String privateKey) { + public void setPrivateKey(@Nullable String privateKey) { this.privateKey = privateKey; } - public String getPrivateKeyPassword() { + public @Nullable String getPrivateKeyPassword() { return this.privateKeyPassword; } - public void setPrivateKeyPassword(String privateKeyPassword) { + public void setPrivateKeyPassword(@Nullable String privateKeyPassword) { this.privateKeyPassword = privateKeyPassword; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java index b53b3496995..1aa94f24cbd 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java @@ -16,6 +16,12 @@ package org.springframework.boot.autoconfigure.ssl; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.List; + +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.ssl.SslBundleProperties.Key; import org.springframework.boot.io.ApplicationResourceLoader; import org.springframework.boot.ssl.SslBundle; @@ -60,11 +66,11 @@ public final class PropertiesSslBundle implements SslBundle { this.managers = SslManagerBundle.from(this.stores, this.key); } - private static SslBundleKey asSslKeyReference(Key key) { + private static SslBundleKey asSslKeyReference(@Nullable Key key) { return (key != null) ? SslBundleKey.of(key.getPassword(), key.getAlias()) : SslBundleKey.NONE; } - private static SslOptions asSslOptions(SslBundleProperties.Options options) { + private static SslOptions asSslOptions(SslBundleProperties.@Nullable Options options) { return (options != null) ? SslOptions.of(options.getCiphers(), options.getEnabledProtocols()) : SslOptions.NONE; } @@ -120,13 +126,18 @@ public final class PropertiesSslBundle implements SslBundle { return new PropertiesSslBundle(storeBundle, properties); } - private static PemSslStore getPemSslStore(String propertyName, PemSslBundleProperties.Store properties, + private static @Nullable PemSslStore getPemSslStore(String propertyName, PemSslBundleProperties.Store properties, ResourceLoader resourceLoader) { PemSslStoreDetails details = asPemSslStoreDetails(properties); PemSslStore pemSslStore = PemSslStore.load(details, resourceLoader); if (properties.isVerifyKeys()) { - CertificateMatcher certificateMatcher = new CertificateMatcher(pemSslStore.privateKey()); - Assert.state(certificateMatcher.matchesAny(pemSslStore.certificates()), + Assert.state(pemSslStore != null, "'pemSslStore' must not be null"); + PrivateKey privateKey = pemSslStore.privateKey(); + Assert.state(privateKey != null, "'privateKey' must not be null"); + CertificateMatcher certificateMatcher = new CertificateMatcher(privateKey); + List certificates = pemSslStore.certificates(); + Assert.state(certificates != null, "'certificates' must not be null"); + Assert.state(certificateMatcher.matchesAny(certificates), () -> "Private key in %s matches none of the certificates in the chain".formatted(propertyName)); } return pemSslStore; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java index 777d4e3def3..f00b4ec0632 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.ssl; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.ssl.SslBundle; /** @@ -79,26 +81,26 @@ public abstract class SslBundleProperties { /** * Supported SSL ciphers. */ - private Set ciphers; + private @Nullable Set ciphers; /** * Enabled SSL protocols. */ - private Set enabledProtocols; + private @Nullable Set enabledProtocols; - public Set getCiphers() { + public @Nullable Set getCiphers() { return this.ciphers; } - public void setCiphers(Set ciphers) { + public void setCiphers(@Nullable Set ciphers) { this.ciphers = ciphers; } - public Set getEnabledProtocols() { + public @Nullable Set getEnabledProtocols() { return this.enabledProtocols; } - public void setEnabledProtocols(Set enabledProtocols) { + public void setEnabledProtocols(@Nullable Set enabledProtocols) { this.enabledProtocols = enabledProtocols; } @@ -109,26 +111,26 @@ public abstract class SslBundleProperties { /** * The password used to access the key in the key store. */ - private String password; + private @Nullable String password; /** * The alias that identifies the key in the key store. */ - private String alias; + private @Nullable String alias; - public String getPassword() { + public @Nullable String getPassword() { return this.password; } - public void setPassword(String password) { + public void setPassword(@Nullable String password) { this.password = password; } - public String getAlias() { + public @Nullable String getAlias() { return this.alias; } - public void setAlias(String alias) { + public void setAlias(@Nullable String alias) { this.alias = alias; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java index 6054cb448a7..1249dc3425e 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for SSL bundles. */ +@NullMarked package org.springframework.boot.autoconfigure.ssl; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java index d18e7a64a72..b2c7d15963a 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.task; import java.time.Duration; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -86,7 +88,7 @@ public class TaskExecutionProperties { * Set the maximum number of parallel accesses allowed. -1 indicates no * concurrency limit at all. */ - private Integer concurrencyLimit; + private @Nullable Integer concurrencyLimit; public boolean isRejectTasksWhenLimitReached() { return this.rejectTasksWhenLimitReached; @@ -96,11 +98,11 @@ public class TaskExecutionProperties { this.rejectTasksWhenLimitReached = rejectTasksWhenLimitReached; } - public Integer getConcurrencyLimit() { + public @Nullable Integer getConcurrencyLimit() { return this.concurrencyLimit; } - public void setConcurrencyLimit(Integer concurrencyLimit) { + public void setConcurrencyLimit(@Nullable Integer concurrencyLimit) { this.concurrencyLimit = concurrencyLimit; } @@ -215,7 +217,7 @@ public class TaskExecutionProperties { /** * Maximum time the executor should wait for remaining tasks to complete. */ - private Duration awaitTerminationPeriod; + private @Nullable Duration awaitTerminationPeriod; public boolean isAwaitTermination() { return this.awaitTermination; @@ -225,11 +227,11 @@ public class TaskExecutionProperties { this.awaitTermination = awaitTermination; } - public Duration getAwaitTerminationPeriod() { + public @Nullable Duration getAwaitTerminationPeriod() { return this.awaitTerminationPeriod; } - public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) { + public void setAwaitTerminationPeriod(@Nullable Duration awaitTerminationPeriod) { this.awaitTerminationPeriod = awaitTerminationPeriod; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java index 23405cc8671..a9687034e64 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java @@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.task; import java.util.List; import java.util.concurrent.Executor; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; @@ -54,7 +56,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; */ class TaskExecutorConfigurations { - private static TaskDecorator getTaskDecorator(ObjectProvider taskDecorator) { + private static @Nullable TaskDecorator getTaskDecorator(ObjectProvider taskDecorator) { List taskDecorators = taskDecorator.orderedStream().toList(); if (taskDecorators.size() == 1) { return taskDecorators.get(0); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java index 437f1dcb3c5..b08df1625fb 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java @@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.task; import java.util.List; import java.util.concurrent.ScheduledExecutorService; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -45,7 +47,7 @@ import org.springframework.scheduling.config.TaskManagementConfigUtils; */ class TaskSchedulingConfigurations { - private static TaskDecorator getTaskDecorator(ObjectProvider taskDecorator) { + private static @Nullable TaskDecorator getTaskDecorator(ObjectProvider taskDecorator) { List taskDecorators = taskDecorator.orderedStream().toList(); if (taskDecorators.size() == 1) { return taskDecorators.get(0); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java index 2c0486de082..0c3eb43ad27 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.task; import java.time.Duration; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -84,13 +86,13 @@ public class TaskSchedulingProperties { * Set the maximum number of parallel accesses allowed. -1 indicates no * concurrency limit at all. */ - private Integer concurrencyLimit; + private @Nullable Integer concurrencyLimit; - public Integer getConcurrencyLimit() { + public @Nullable Integer getConcurrencyLimit() { return this.concurrencyLimit; } - public void setConcurrencyLimit(Integer concurrencyLimit) { + public void setConcurrencyLimit(@Nullable Integer concurrencyLimit) { this.concurrencyLimit = concurrencyLimit; } @@ -106,7 +108,7 @@ public class TaskSchedulingProperties { /** * Maximum time the executor should wait for remaining tasks to complete. */ - private Duration awaitTerminationPeriod; + private @Nullable Duration awaitTerminationPeriod; public boolean isAwaitTermination() { return this.awaitTermination; @@ -116,11 +118,11 @@ public class TaskSchedulingProperties { this.awaitTermination = awaitTermination; } - public Duration getAwaitTerminationPeriod() { + public @Nullable Duration getAwaitTerminationPeriod() { return this.awaitTerminationPeriod; } - public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) { + public void setAwaitTerminationPeriod(@Nullable Duration awaitTerminationPeriod) { this.awaitTerminationPeriod = awaitTerminationPeriod; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/package-info.java index 0a28a62ad13..00965f9dd7b 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for task execution and scheduling. */ +@NullMarked package org.springframework.boot.autoconfigure.task; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProviders.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProviders.java index 272492cceff..3baa48fc2d5 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProviders.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProviders.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jspecify.annotations.Nullable; + import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; @@ -72,7 +74,14 @@ public class TemplateAvailabilityProviders { * @param applicationContext the source application context */ public TemplateAvailabilityProviders(ApplicationContext applicationContext) { - this((applicationContext != null) ? applicationContext.getClassLoader() : null); + this(getClassLoader(applicationContext)); + } + + private static ClassLoader getClassLoader(ApplicationContext applicationContext) { + Assert.notNull(applicationContext, "'applicationContext' must not be null"); + ClassLoader classLoader = applicationContext.getClassLoader(); + Assert.state(classLoader != null, "'classLoader' must not be null"); + return classLoader; } /** @@ -107,10 +116,11 @@ public class TemplateAvailabilityProviders { * @param applicationContext the application context * @return a {@link TemplateAvailabilityProvider} or null */ - public TemplateAvailabilityProvider getProvider(String view, ApplicationContext applicationContext) { + public @Nullable TemplateAvailabilityProvider getProvider(String view, ApplicationContext applicationContext) { Assert.notNull(applicationContext, "'applicationContext' must not be null"); - return getProvider(view, applicationContext.getEnvironment(), applicationContext.getClassLoader(), - applicationContext); + ClassLoader classLoader = applicationContext.getClassLoader(); + Assert.state(classLoader != null, "'classLoader' must not be null"); + return getProvider(view, applicationContext.getEnvironment(), classLoader, applicationContext); } /** @@ -121,8 +131,8 @@ public class TemplateAvailabilityProviders { * @param resourceLoader the resource loader * @return a {@link TemplateAvailabilityProvider} or null */ - public TemplateAvailabilityProvider getProvider(String view, Environment environment, ClassLoader classLoader, - ResourceLoader resourceLoader) { + public @Nullable TemplateAvailabilityProvider getProvider(String view, Environment environment, + ClassLoader classLoader, ResourceLoader resourceLoader) { Assert.notNull(view, "'view' must not be null"); Assert.notNull(environment, "'environment' must not be null"); Assert.notNull(classLoader, "'classLoader' must not be null"); @@ -143,8 +153,8 @@ public class TemplateAvailabilityProviders { return (provider != NONE) ? provider : null; } - private TemplateAvailabilityProvider findProvider(String view, Environment environment, ClassLoader classLoader, - ResourceLoader resourceLoader) { + private @Nullable TemplateAvailabilityProvider findProvider(String view, Environment environment, + ClassLoader classLoader, ResourceLoader resourceLoader) { for (TemplateAvailabilityProvider candidate : this.providers) { if (candidate.isTemplateAvailable(view, environment, classLoader, resourceLoader)) { return candidate; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateRuntimeHints.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateRuntimeHints.java index 6ef6a559834..84888478c2f 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateRuntimeHints.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateRuntimeHints.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.template; +import org.jspecify.annotations.Nullable; + import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -27,7 +29,7 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar; class TemplateRuntimeHints implements RuntimeHintsRegistrar { @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { hints.resources().registerPatternIfPresent(classLoader, "templates", (hint) -> hint.includes("templates/**")); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/package-info.java index 7aa8bd07f0c..57e2136c385 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/package-info.java @@ -17,4 +17,7 @@ /** * Base classes for template Auto-configuration. */ +@NullMarked package org.springframework.boot.autoconfigure.template; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/OnEnabledResourceChainCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/OnEnabledResourceChainCondition.java index 338b371702e..96e4b98a06e 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/OnEnabledResourceChainCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/OnEnabledResourceChainCondition.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.web; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; @@ -24,6 +26,7 @@ import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.lang.Contract; import org.springframework.util.ClassUtils; /** @@ -64,8 +67,13 @@ class OnEnabledResourceChainCondition extends SpringBootCondition { return ConditionOutcome.noMatch(message.because("disabled")); } - private Boolean getEnabledProperty(ConfigurableEnvironment environment, String key, Boolean defaultValue) { + @Contract("_, _, !null -> !null") + private @Nullable Boolean getEnabledProperty(ConfigurableEnvironment environment, String key, + @Nullable Boolean defaultValue) { String name = "spring.web.resources.chain." + key + "enabled"; + if (defaultValue == null) { + return environment.getProperty(name, Boolean.class); + } return environment.getProperty(name, Boolean.class, defaultValue); } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebProperties.java index 70ebcabb41b..bf09416c94e 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebProperties.java @@ -21,6 +21,8 @@ import java.time.temporal.ChronoUnit; import java.util.Locale; import java.util.concurrent.TimeUnit; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.convert.DurationUnit; @@ -39,7 +41,7 @@ public class WebProperties { * Locale to use. By default, this locale is overridden by the "Accept-Language" * header. */ - private Locale locale; + private @Nullable Locale locale; /** * Define how the locale should be resolved. @@ -48,11 +50,11 @@ public class WebProperties { private final Resources resources = new Resources(); - public Locale getLocale() { + public @Nullable Locale getLocale() { return this.locale; } - public void setLocale(Locale locale) { + public void setLocale(@Nullable Locale locale) { this.locale = locale; } @@ -155,7 +157,7 @@ public class WebProperties { * Whether to enable the Spring Resource Handling chain. By default, disabled * unless at least one strategy has been enabled. */ - private Boolean enabled; + private @Nullable Boolean enabled; /** * Whether to enable caching in the Resource chain. @@ -177,7 +179,7 @@ public class WebProperties { * @return whether the resource chain is enabled or {@code null} if no * specified settings are present. */ - public Boolean getEnabled() { + public @Nullable Boolean getEnabled() { return getEnabled(getStrategy().getFixed().isEnabled(), getStrategy().getContent().isEnabled(), this.enabled); } @@ -213,7 +215,8 @@ public class WebProperties { this.customized = true; } - static Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled, Boolean chainEnabled) { + static @Nullable Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled, + @Nullable Boolean chainEnabled) { return (fixedEnabled || contentEnabled) ? Boolean.TRUE : chainEnabled; } @@ -299,7 +302,7 @@ public class WebProperties { /** * Version string to use for the fixed Version Strategy. */ - private String version; + private @Nullable String version; public boolean isEnabled() { return this.enabled; @@ -319,11 +322,11 @@ public class WebProperties { this.paths = paths; } - public String getVersion() { + public @Nullable String getVersion() { return this.version; } - public void setVersion(String version) { + public void setVersion(@Nullable String version) { this.customized = true; this.version = version; } @@ -351,7 +354,7 @@ public class WebProperties { * by the 'spring.web.resources.cache.cachecontrol' properties. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration period; + private @Nullable Duration period; /** * Cache control HTTP headers, only allows valid directive combinations. @@ -365,11 +368,11 @@ public class WebProperties { */ private boolean useLastModified = true; - public Duration getPeriod() { + public @Nullable Duration getPeriod() { return this.period; } - public void setPeriod(Duration period) { + public void setPeriod(@Nullable Duration period) { this.customized = true; this.period = period; } @@ -402,169 +405,169 @@ public class WebProperties { * suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration maxAge; + private @Nullable Duration maxAge; /** * Indicate that the cached response can be reused only if re-validated * with the server. */ - private Boolean noCache; + private @Nullable Boolean noCache; /** * Indicate to not cache the response in any case. */ - private Boolean noStore; + private @Nullable Boolean noStore; /** * Indicate that once it has become stale, a cache must not use the * response without re-validating it with the server. */ - private Boolean mustRevalidate; + private @Nullable Boolean mustRevalidate; /** * Indicate intermediaries (caches and others) that they should not * transform the response content. */ - private Boolean noTransform; + private @Nullable Boolean noTransform; /** * Indicate that any cache may store the response. */ - private Boolean cachePublic; + private @Nullable Boolean cachePublic; /** * Indicate that the response message is intended for a single user and * must not be stored by a shared cache. */ - private Boolean cachePrivate; + private @Nullable Boolean cachePrivate; /** * Same meaning as the "must-revalidate" directive, except that it does * not apply to private caches. */ - private Boolean proxyRevalidate; + private @Nullable Boolean proxyRevalidate; /** * Maximum time the response can be served after it becomes stale, in * seconds if no duration suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration staleWhileRevalidate; + private @Nullable Duration staleWhileRevalidate; /** * Maximum time the response may be used when errors are encountered, in * seconds if no duration suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration staleIfError; + private @Nullable Duration staleIfError; /** * Maximum time the response should be cached by shared caches, in seconds * if no duration suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) - private Duration sMaxAge; + private @Nullable Duration sMaxAge; - public Duration getMaxAge() { + public @Nullable Duration getMaxAge() { return this.maxAge; } - public void setMaxAge(Duration maxAge) { + public void setMaxAge(@Nullable Duration maxAge) { this.customized = true; this.maxAge = maxAge; } - public Boolean getNoCache() { + public @Nullable Boolean getNoCache() { return this.noCache; } - public void setNoCache(Boolean noCache) { + public void setNoCache(@Nullable Boolean noCache) { this.customized = true; this.noCache = noCache; } - public Boolean getNoStore() { + public @Nullable Boolean getNoStore() { return this.noStore; } - public void setNoStore(Boolean noStore) { + public void setNoStore(@Nullable Boolean noStore) { this.customized = true; this.noStore = noStore; } - public Boolean getMustRevalidate() { + public @Nullable Boolean getMustRevalidate() { return this.mustRevalidate; } - public void setMustRevalidate(Boolean mustRevalidate) { + public void setMustRevalidate(@Nullable Boolean mustRevalidate) { this.customized = true; this.mustRevalidate = mustRevalidate; } - public Boolean getNoTransform() { + public @Nullable Boolean getNoTransform() { return this.noTransform; } - public void setNoTransform(Boolean noTransform) { + public void setNoTransform(@Nullable Boolean noTransform) { this.customized = true; this.noTransform = noTransform; } - public Boolean getCachePublic() { + public @Nullable Boolean getCachePublic() { return this.cachePublic; } - public void setCachePublic(Boolean cachePublic) { + public void setCachePublic(@Nullable Boolean cachePublic) { this.customized = true; this.cachePublic = cachePublic; } - public Boolean getCachePrivate() { + public @Nullable Boolean getCachePrivate() { return this.cachePrivate; } - public void setCachePrivate(Boolean cachePrivate) { + public void setCachePrivate(@Nullable Boolean cachePrivate) { this.customized = true; this.cachePrivate = cachePrivate; } - public Boolean getProxyRevalidate() { + public @Nullable Boolean getProxyRevalidate() { return this.proxyRevalidate; } - public void setProxyRevalidate(Boolean proxyRevalidate) { + public void setProxyRevalidate(@Nullable Boolean proxyRevalidate) { this.customized = true; this.proxyRevalidate = proxyRevalidate; } - public Duration getStaleWhileRevalidate() { + public @Nullable Duration getStaleWhileRevalidate() { return this.staleWhileRevalidate; } - public void setStaleWhileRevalidate(Duration staleWhileRevalidate) { + public void setStaleWhileRevalidate(@Nullable Duration staleWhileRevalidate) { this.customized = true; this.staleWhileRevalidate = staleWhileRevalidate; } - public Duration getStaleIfError() { + public @Nullable Duration getStaleIfError() { return this.staleIfError; } - public void setStaleIfError(Duration staleIfError) { + public void setStaleIfError(@Nullable Duration staleIfError) { this.customized = true; this.staleIfError = staleIfError; } - public Duration getSMaxAge() { + public @Nullable Duration getSMaxAge() { return this.sMaxAge; } - public void setSMaxAge(Duration sMaxAge) { + public void setSMaxAge(@Nullable Duration sMaxAge) { this.customized = true; this.sMaxAge = sMaxAge; } - public CacheControl toHttpCacheControl() { + public @Nullable CacheControl toHttpCacheControl() { PropertyMapper map = PropertyMapper.get(); CacheControl control = createCacheControl(); map.from(this::getMustRevalidate).whenTrue().toCall(control::mustRevalidate); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebResourcesRuntimeHints.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebResourcesRuntimeHints.java index 2451433563a..d299454c072 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebResourcesRuntimeHints.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebResourcesRuntimeHints.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.web; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -33,7 +35,7 @@ public class WebResourcesRuntimeHints implements RuntimeHintsRegistrar { "public/"); @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = (classLoader != null) ? classLoader : getClass().getClassLoader(); String[] locations = DEFAULT_LOCATIONS.stream() .filter((candidate) -> classLoaderToUse.getResource(candidate) != null) diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java index efc1377c074..e41765ea9cc 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java @@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.web.format; import java.time.format.DateTimeFormatter; import java.time.format.ResolverStyle; +import org.jspecify.annotations.Nullable; + import org.springframework.util.StringUtils; /** @@ -30,13 +32,13 @@ import org.springframework.util.StringUtils; */ public class DateTimeFormatters { - private DateTimeFormatter dateFormatter; + private @Nullable DateTimeFormatter dateFormatter; - private String datePattern; + private @Nullable String datePattern; - private DateTimeFormatter timeFormatter; + private @Nullable DateTimeFormatter timeFormatter; - private DateTimeFormatter dateTimeFormatter; + private @Nullable DateTimeFormatter dateTimeFormatter; /** * Configures the date format using the given {@code pattern}. @@ -77,19 +79,19 @@ public class DateTimeFormatters { return this; } - DateTimeFormatter getDateFormatter() { + @Nullable DateTimeFormatter getDateFormatter() { return this.dateFormatter; } - String getDatePattern() { + @Nullable String getDatePattern() { return this.datePattern; } - DateTimeFormatter getTimeFormatter() { + @Nullable DateTimeFormatter getTimeFormatter() { return this.timeFormatter; } - DateTimeFormatter getDateTimeFormatter() { + @Nullable DateTimeFormatter getDateTimeFormatter() { return this.dateTimeFormatter; } @@ -97,7 +99,7 @@ public class DateTimeFormatters { return this.dateFormatter != null || this.timeFormatter != null || this.dateTimeFormatter != null; } - private static DateTimeFormatter formatter(String pattern) { + private static @Nullable DateTimeFormatter formatter(String pattern) { return StringUtils.hasText(pattern) ? DateTimeFormatter.ofPattern(pattern).withResolverStyle(ResolverStyle.SMART) : null; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java index 186bd586d10..06826ddb9e0 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java @@ -20,6 +20,8 @@ import java.time.format.DateTimeFormatter; import java.util.function.Consumer; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; + import org.springframework.format.datetime.DateFormatter; import org.springframework.format.datetime.DateFormatterRegistrar; import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; @@ -79,7 +81,7 @@ public class WebConversionService extends DefaultFormattingConversionService { dateTime.registerFormatters(this); } - private void configure(Supplier supplier, Consumer consumer) { + private void configure(Supplier<@Nullable DateTimeFormatter> supplier, Consumer consumer) { DateTimeFormatter formatter = supplier.get(); if (formatter != null) { consumer.accept(formatter); diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/package-info.java index 4d1aa32825a..1ffb53e4c79 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/package-info.java @@ -17,4 +17,7 @@ /** * Support classes for web-specific formatting. */ +@NullMarked package org.springframework.boot.autoconfigure.web.format; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/package-info.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/package-info.java index 7d726c37448..3fbbdc991b3 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/package-info.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for common web concerns. */ +@NullMarked package org.springframework.boot.autoconfigure.web; + +import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProvidersTests.java b/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProvidersTests.java index d23c162d9ed..bb5dcd95d15 100644 --- a/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProvidersTests.java +++ b/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/template/TemplateAvailabilityProvidersTests.java @@ -73,7 +73,7 @@ class TemplateAvailabilityProvidersTests { void createWhenApplicationContextIsNullShouldThrowException() { assertThatIllegalArgumentException() .isThrownBy(() -> new TemplateAvailabilityProviders((ApplicationContext) null)) - .withMessageContaining("'classLoader' must not be null"); + .withMessageContaining("'applicationContext' must not be null"); } @Test