Browse Source

Improve null-safety of core/spring-boot-autoconfigure

See gh-46926
pull/46973/head
Moritz Halbritter 5 months ago
parent
commit
c76b087025
  1. 10
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java
  2. 4
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java
  3. 43
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java
  4. 9
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.java
  5. 78
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java
  6. 4
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java

10
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java

@ -27,7 +27,6 @@ import org.springframework.context.annotation.Configuration; @@ -27,7 +27,6 @@ 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.
@ -66,12 +65,13 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad @@ -66,12 +65,13 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
List<String> autoConfigurations = this.autoConfigurations;
if (autoConfigurations == null) {
ImportCandidates importCandidates = ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader);
this.autoConfigurations = importCandidates.getCandidates();
autoConfigurations = importCandidates.getCandidates();
this.autoConfigurations = autoConfigurations;
}
Assert.state(this.autoConfigurations != null, "'autoConfigurations' must not be null");
return this.autoConfigurations;
return autoConfigurations;
}
}

4
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java

@ -110,8 +110,10 @@ public abstract class AutoConfigurationPackages { @@ -110,8 +110,10 @@ public abstract class AutoConfigurationPackages {
ValueHolder indexedArgumentValue = constructorArgumentValues.getIndexedArgumentValue(0, String[].class);
Assert.state(indexedArgumentValue != null, "'indexedArgumentValue' must not be null");
String[] existingPackages = (String[]) indexedArgumentValue.getValue();
Stream<String> existingPackagesStream = (existingPackages != null) ? Stream.of(existingPackages)
: Stream.empty();
constructorArgumentValues.addIndexedArgumentValue(0,
Stream.concat(Stream.of(existingPackages), Stream.of(additionalBasePackages))
Stream.concat(existingPackagesStream, Stream.of(additionalBasePackages))
.distinct()
.toArray(String[]::new));
}

43
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java

@ -202,17 +202,21 @@ class AutoConfigurationSorter { @@ -202,17 +202,21 @@ class AutoConfigurationSorter {
}
Set<String> getBefore() {
if (this.before == null) {
this.before = getClassNames("AutoConfigureBefore", AutoConfigureBefore.class);
Set<String> before = this.before;
if (before == null) {
before = getClassNames("AutoConfigureBefore", AutoConfigureBefore.class);
this.before = before;
}
return this.before;
return before;
}
Set<String> getAfter() {
if (this.after == null) {
this.after = getClassNames("AutoConfigureAfter", AutoConfigureAfter.class);
Set<String> after = this.after;
if (after == null) {
after = getClassNames("AutoConfigureAfter", AutoConfigureAfter.class);
this.after = after;
}
return this.after;
return after;
}
private Set<String> getClassNames(String metadataKey, Class<? extends Annotation> annotation) {
@ -244,7 +248,12 @@ class AutoConfigurationSorter { @@ -244,7 +248,12 @@ class AutoConfigurationSorter {
}
Map<String, @Nullable Object> attributes = getAnnotationMetadata()
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (attributes != null) ? (Integer) attributes.get("value") : AutoConfigureOrder.DEFAULT_ORDER;
if (attributes != null) {
Integer value = (Integer) attributes.get("value");
Assert.state(value != null, "'value' must not be null");
return value;
}
return AutoConfigureOrder.DEFAULT_ORDER;
}
private boolean wasProcessed() {
@ -258,23 +267,29 @@ class AutoConfigurationSorter { @@ -258,23 +267,29 @@ class AutoConfigurationSorter {
if (attributes == null) {
return Collections.emptySet();
}
Set<String> value = new LinkedHashSet<>();
Collections.addAll(value, (String[]) attributes.get("value"));
Collections.addAll(value, (String[]) attributes.get("name"));
return value;
Set<String> result = new LinkedHashSet<>();
String[] value = (String[]) attributes.get("value");
String[] name = (String[]) attributes.get("name");
Assert.state(value != null, "'value' must not be null");
Assert.state(name != null, "'name' must not be null");
Collections.addAll(result, value);
Collections.addAll(result, name);
return result;
}
private AnnotationMetadata getAnnotationMetadata() {
if (this.annotationMetadata == null) {
AnnotationMetadata annotationMetadata = this.annotationMetadata;
if (annotationMetadata == null) {
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(this.className);
this.annotationMetadata = metadataReader.getAnnotationMetadata();
annotationMetadata = metadataReader.getAnnotationMetadata();
this.annotationMetadata = annotationMetadata;
}
catch (IOException ex) {
throw new IllegalStateException("Unable to read meta-data for class " + this.className, ex);
}
}
return this.annotationMetadata;
return annotationMetadata;
}
}

9
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.java

@ -55,13 +55,14 @@ abstract class FilteringSpringBootCondition extends SpringBootCondition @@ -55,13 +55,14 @@ abstract class FilteringSpringBootCondition extends SpringBootCondition
@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) {
ConditionOutcome outcome = outcomes[i];
match[i] = (outcome == null || outcome.isMatch());
if (!match[i] && outcome != null) {
String autoConfigurationClass = autoConfigurationClasses[i];
Assert.state(autoConfigurationClass != null, "'autoConfigurationClass' must not be null");
logOutcome(autoConfigurationClass, outcomes[i]);
logOutcome(autoConfigurationClass, outcome);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClass, this, outcomes[i]);
report.recordConditionEvaluation(autoConfigurationClass, this, outcome);
}
}
}

78
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java

@ -47,6 +47,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -47,6 +47,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
@ -177,7 +178,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -177,7 +178,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
}
ConfigurableListableBeanFactory beanFactory = spec.context.getBeanFactory();
Assert.state(beanFactory != null, "'beanFactory' must not be null");
Map<String, BeanDefinition> beanDefinitions = getBeanDefinitions(beanFactory, allBeans,
Map<String, @Nullable BeanDefinition> beanDefinitions = getBeanDefinitions(beanFactory, allBeans,
spec.getStrategy() == SearchStrategy.ALL);
List<String> primaryBeans = getPrimaryBeans(beanDefinitions);
if (primaryBeans.size() == 1) {
@ -217,7 +218,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -217,7 +218,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(beanFactory, considerHierarchy,
spec.getIgnoredTypes(), parameterizedContainers);
for (ResolvableType type : spec.getTypes()) {
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(beanFactory,
Map<String, @Nullable BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(beanFactory,
considerHierarchy, type, parameterizedContainers);
Set<String> typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions,
(name, definition) -> !ScopedProxyUtils.isScopedTarget(name)
@ -230,8 +231,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -230,8 +231,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
}
}
for (String annotation : spec.getAnnotations()) {
Map<String, BeanDefinition> annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(classLoader,
beanFactory, annotation, considerHierarchy);
Map<String, @Nullable BeanDefinition> annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(
classLoader, beanFactory, annotation, considerHierarchy);
Set<String> annotationMatchedNames = matchedNamesFrom(annotationMatchedDefinitions,
(name, definition) -> isCandidate(beanFactory, name, definition, beansIgnoredByType));
if (annotationMatchedNames.isEmpty()) {
@ -265,7 +266,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -265,7 +266,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return beanFactory;
}
private Set<String> matchedNamesFrom(Map<String, BeanDefinition> namedDefinitions,
private Set<String> matchedNamesFrom(Map<String, @Nullable BeanDefinition> namedDefinitions,
BiPredicate<String, BeanDefinition> filter) {
Set<String> matchedNames = new LinkedHashSet<>(namedDefinitions.size());
for (Entry<String, BeanDefinition> namedDefinition : namedDefinitions.entrySet()) {
@ -316,16 +317,16 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -316,16 +317,16 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return (result != null) ? result : Collections.emptySet();
}
private Map<String, BeanDefinition> getBeanDefinitionsForType(ListableBeanFactory beanFactory,
private Map<String, @Nullable BeanDefinition> getBeanDefinitionsForType(ListableBeanFactory beanFactory,
boolean considerHierarchy, ResolvableType type, Set<ResolvableType> parameterizedContainers) {
Map<String, BeanDefinition> result = collectBeanDefinitionsForType(beanFactory, considerHierarchy, type,
parameterizedContainers, null);
return (result != null) ? result : Collections.emptyMap();
Map<String, @Nullable BeanDefinition> result = collectBeanDefinitionsForType(beanFactory, considerHierarchy,
type, parameterizedContainers, null);
return (result != null) ? result : Collections.<String, @Nullable BeanDefinition>emptyMap();
}
private @Nullable Map<String, BeanDefinition> collectBeanDefinitionsForType(ListableBeanFactory beanFactory,
boolean considerHierarchy, ResolvableType type, Set<ResolvableType> parameterizedContainers,
@Nullable Map<String, BeanDefinition> result) {
private @Nullable Map<String, @Nullable BeanDefinition> collectBeanDefinitionsForType(
ListableBeanFactory beanFactory, boolean considerHierarchy, ResolvableType type,
Set<ResolvableType> parameterizedContainers, @Nullable Map<String, @Nullable BeanDefinition> result) {
result = putAll(result, beanFactory.getBeanNamesForType(type, true, false), beanFactory);
for (ResolvableType parameterizedContainer : parameterizedContainers) {
Class<?> resolved = parameterizedContainer.resolve();
@ -343,9 +344,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -343,9 +344,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return result;
}
private Map<String, BeanDefinition> getBeanDefinitionsForAnnotation(@Nullable ClassLoader classLoader,
private Map<String, @Nullable BeanDefinition> getBeanDefinitionsForAnnotation(@Nullable ClassLoader classLoader,
ConfigurableListableBeanFactory beanFactory, String type, boolean considerHierarchy) throws LinkageError {
Map<String, BeanDefinition> result = null;
Map<String, @Nullable BeanDefinition> result = null;
try {
result = collectBeanDefinitionsForAnnotation(beanFactory, resolveAnnotationType(classLoader, type),
considerHierarchy, result);
@ -353,7 +354,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -353,7 +354,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
catch (ClassNotFoundException ex) {
// Continue
}
return (result != null) ? result : Collections.emptyMap();
return (result != null) ? result : Collections.<String, @Nullable BeanDefinition>emptyMap();
}
@SuppressWarnings("unchecked")
@ -362,9 +363,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -362,9 +363,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return (Class<? extends Annotation>) resolve(type, classLoader);
}
private @Nullable Map<String, BeanDefinition> collectBeanDefinitionsForAnnotation(ListableBeanFactory beanFactory,
Class<? extends Annotation> annotationType, boolean considerHierarchy,
@Nullable Map<String, BeanDefinition> result) {
private @Nullable Map<String, @Nullable BeanDefinition> collectBeanDefinitionsForAnnotation(
ListableBeanFactory beanFactory, Class<? extends Annotation> annotationType, boolean considerHierarchy,
@Nullable Map<String, @Nullable BeanDefinition> result) {
result = putAll(result, getBeanNamesForAnnotation(beanFactory, annotationType), beanFactory);
if (considerHierarchy) {
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
@ -459,9 +460,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -459,9 +460,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
}
}
private Map<String, BeanDefinition> getBeanDefinitions(ConfigurableListableBeanFactory beanFactory,
private Map<String, @Nullable BeanDefinition> getBeanDefinitions(ConfigurableListableBeanFactory beanFactory,
Set<String> beanNames, boolean considerHierarchy) {
Map<String, BeanDefinition> definitions = new HashMap<>(beanNames.size());
Map<String, @Nullable BeanDefinition> definitions = new HashMap<>(beanNames.size());
for (String beanName : beanNames) {
BeanDefinition beanDefinition = findBeanDefinition(beanFactory, beanName, considerHierarchy);
definitions.put(beanName, beanDefinition);
@ -469,17 +470,20 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -469,17 +470,20 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return definitions;
}
private List<String> getPrimaryBeans(Map<String, BeanDefinition> beanDefinitions) {
return getMatchingBeans(beanDefinitions, BeanDefinition::isPrimary);
private List<String> getPrimaryBeans(Map<String, @Nullable BeanDefinition> beanDefinitions) {
return getMatchingBeans(beanDefinitions,
(beanDefinition) -> beanDefinition != null && beanDefinition.isPrimary());
}
private List<String> getNonFallbackBeans(Map<String, BeanDefinition> beanDefinitions) {
return getMatchingBeans(beanDefinitions, Predicate.not(BeanDefinition::isFallback));
private List<String> getNonFallbackBeans(Map<String, @Nullable BeanDefinition> beanDefinitions) {
return getMatchingBeans(beanDefinitions,
Predicate.not((beanDefinition) -> beanDefinition != null && beanDefinition.isFallback()));
}
private List<String> getMatchingBeans(Map<String, BeanDefinition> beanDefinitions, Predicate<BeanDefinition> test) {
private List<String> getMatchingBeans(Map<String, @Nullable BeanDefinition> beanDefinitions,
Predicate<@Nullable BeanDefinition> test) {
List<String> matches = new ArrayList<>();
for (Entry<String, BeanDefinition> namedBeanDefinition : beanDefinitions.entrySet()) {
for (Entry<String, @Nullable BeanDefinition> namedBeanDefinition : beanDefinitions.entrySet()) {
if (test.test(namedBeanDefinition.getValue())) {
matches.add(namedBeanDefinition.getKey());
}
@ -508,8 +512,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -508,8 +512,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return result;
}
private static @Nullable Map<String, BeanDefinition> putAll(@Nullable Map<String, BeanDefinition> result,
String[] beanNames, ListableBeanFactory beanFactory) {
private static @Nullable Map<String, @Nullable BeanDefinition> putAll(
@Nullable Map<String, @Nullable BeanDefinition> result, String[] beanNames,
ListableBeanFactory beanFactory) {
if (ObjectUtils.isEmpty(beanNames)) {
return result;
}
@ -563,7 +568,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -563,7 +568,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
Spec(ConditionContext context, AnnotatedTypeMetadata metadata, MergedAnnotations annotations,
Class<A> annotationType) {
MultiValueMap<String, Object> attributes = annotations.stream(annotationType)
MultiValueMap<String, @Nullable Object> attributes = annotations.stream(annotationType)
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.collect(MergedAnnotationCollectors.toMultiValueMap(Adapt.CLASS_TO_STRING));
MergedAnnotation<A> annotation = annotations.get(annotationType);
@ -588,17 +593,18 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -588,17 +593,18 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
validate(deductionException);
}
protected Set<String> extractTypes(MultiValueMap<String, Object> attributes) {
protected Set<String> extractTypes(@Nullable MultiValueMap<String, @Nullable Object> attributes) {
return extract(attributes, "value", "type");
}
private Set<String> extract(MultiValueMap<String, Object> attributes, String... attributeNames) {
if (attributes.isEmpty()) {
private Set<String> extract(@Nullable MultiValueMap<String, @Nullable Object> attributes,
String... attributeNames) {
if (CollectionUtils.isEmpty(attributes)) {
return Collections.emptySet();
}
Set<String> result = new LinkedHashSet<>();
for (String attributeName : attributeNames) {
List<Object> values = attributes.getOrDefault(attributeName, Collections.emptyList());
List<@Nullable Object> values = attributes.getOrDefault(attributeName, Collections.emptyList());
for (Object value : values) {
if (value instanceof String[] stringArray) {
merge(result, stringArray);
@ -743,11 +749,11 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -743,11 +749,11 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return this.parameterizedContainers;
}
private ConditionMessage.Builder message() {
private Builder message() {
return ConditionMessage.forCondition(this.annotationType, this);
}
private ConditionMessage.Builder message(ConditionMessage message) {
private Builder message(ConditionMessage message) {
return message.andCondition(this.annotationType, this);
}
@ -796,7 +802,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat @@ -796,7 +802,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
}
@Override
protected Set<String> extractTypes(MultiValueMap<String, Object> attributes) {
protected Set<String> extractTypes(@Nullable MultiValueMap<String, @Nullable Object> attributes) {
Set<String> types = super.extractTypes(attributes);
types.removeAll(FILTERED_TYPES);
return types;

4
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java

@ -129,7 +129,9 @@ class OnClassCondition extends FilteringSpringBootCondition { @@ -129,7 +129,9 @@ class OnClassCondition extends FilteringSpringBootCondition {
private void addAll(List<String> list, @Nullable List<@Nullable Object> itemsToAdd) {
if (itemsToAdd != null) {
for (Object item : itemsToAdd) {
Collections.addAll(list, (String[]) item);
if (item != null) {
Collections.addAll(list, (String[]) item);
}
}
}
}

Loading…
Cancel
Save