mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-27 12:39:03 +00:00
Improve null-safety of core/spring-boot-autoconfigure
See gh-46926
This commit is contained in:
+5
-5
@@ -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
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-1
@@ -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));
|
||||
}
|
||||
|
||||
+29
-14
@@ -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 {
|
||||
}
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-36
@@ -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
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user