Order class filter checks and exit early

Update the logic in `OnClassCondition` so that filtering exits on the
first missing class. Also refactor the implementation to save
unnecessary `Set` creation when there is just a single class to check.

The `AutoConfigureAnnotationProcessor` has also been updated to order
classes so that any starting `org.springframework` are considered last.
The assumption being that other classes are more likely to be missing.

Closes gh-12131
This commit is contained in:
Phillip Webb
2018-10-10 21:15:32 -07:00
parent b1d4cf4ea8
commit 85f86243c9
5 changed files with 83 additions and 37 deletions

View File

@@ -20,7 +20,6 @@ import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
@@ -31,6 +30,7 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* {@link Condition} and {@link AutoConfigurationImportFilter} that checks for the
@@ -200,8 +200,8 @@ class OnClassCondition extends FilteringSpringBootCondition {
for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
Set<String> candidates = autoConfigurationMetadata
.getSet(autoConfigurationClass, "ConditionalOnClass");
String candidates = autoConfigurationMetadata
.get(autoConfigurationClass, "ConditionalOnClass");
if (candidates != null) {
outcomes[i - start] = getOutcome(candidates);
}
@@ -210,15 +210,19 @@ class OnClassCondition extends FilteringSpringBootCondition {
return outcomes;
}
private ConditionOutcome getOutcome(Set<String> candidates) {
private ConditionOutcome getOutcome(String candidates) {
try {
List<String> missing = filter(candidates, ClassNameFilter.MISSING,
this.beanClassLoader);
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes")
.items(Style.QUOTE, missing));
if (!candidates.contains(",")) {
return getOutcome(candidates, ClassNameFilter.MISSING,
this.beanClassLoader);
}
for (String candidate : StringUtils
.commaDelimitedListToStringArray(candidates)) {
ConditionOutcome outcome = getOutcome(candidate,
ClassNameFilter.MISSING, this.beanClassLoader);
if (outcome != null) {
return outcome;
}
}
}
catch (Exception ex) {
@@ -227,6 +231,16 @@ class OnClassCondition extends FilteringSpringBootCondition {
return null;
}
private ConditionOutcome getOutcome(String className,
ClassNameFilter classNameFilter, ClassLoader classLoader) {
if (classNameFilter.matches(className, classLoader)) {
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnClass.class)
.didNotFind("required class").items(Style.QUOTE, className));
}
return null;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.condition;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
@@ -75,11 +73,11 @@ public class OnClassConditionAutoConfigurationImportFilterTests {
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
AutoConfigurationMetadata metadata = mock(AutoConfigurationMetadata.class);
given(metadata.wasProcessed("test.match")).willReturn(true);
given(metadata.getSet("test.match", "ConditionalOnClass"))
.willReturn(Collections.singleton("java.io.InputStream"));
given(metadata.get("test.match", "ConditionalOnClass"))
.willReturn("java.io.InputStream");
given(metadata.wasProcessed("test.nomatch")).willReturn(true);
given(metadata.getSet("test.nomatch", "ConditionalOnClass"))
.willReturn(Collections.singleton("java.io.DoesNotExist"));
given(metadata.get("test.nomatch", "ConditionalOnClass"))
.willReturn("java.io.DoesNotExist");
return metadata;
}