Expand configuration class eager filtering to imports
Previously, only root auto-configuration classes could be excluded eagerly via an AutoConfigurationImportFilter. Any configuration class loaded as a result of processing a particular auto-configuration were parsed and checked as usual. This commit makes use of the `getExclusionFilter` callback to expand this filter to all candidates that are considered. The annotation processor has also be expanded to generate metadata for non-root configuration classes. Closes gh-12157
This commit is contained in:
@@ -27,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -88,27 +89,33 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private ConfigurationClassFilter configurationClassFilter;
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata annotationMetadata) {
|
||||
if (!isEnabled(annotationMetadata)) {
|
||||
return NO_IMPORTS;
|
||||
}
|
||||
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
|
||||
.loadMetadata(this.beanClassLoader);
|
||||
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
|
||||
annotationMetadata);
|
||||
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
|
||||
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<String> getExclusionFilter() {
|
||||
return this::shouldExclude;
|
||||
}
|
||||
|
||||
private boolean shouldExclude(String configurationClassName) {
|
||||
return getConfigurationClassFilter().filter(Collections.singletonList(configurationClassName)).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
|
||||
* of the importing {@link Configuration @Configuration} class.
|
||||
* @param autoConfigurationMetadata the auto-configuration metadata
|
||||
* @param annotationMetadata the annotation metadata of the configuration class
|
||||
* @return the auto-configurations that should be imported
|
||||
*/
|
||||
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
|
||||
if (!isEnabled(annotationMetadata)) {
|
||||
return EMPTY_ENTRY;
|
||||
}
|
||||
@@ -118,7 +125,7 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
|
||||
checkExcludedClasses(configurations, exclusions);
|
||||
configurations.removeAll(exclusions);
|
||||
configurations = filter(configurations, autoConfigurationMetadata);
|
||||
configurations = getConfigurationClassFilter().filter(configurations);
|
||||
fireAutoConfigurationImportEvents(configurations, exclusions);
|
||||
return new AutoConfigurationEntry(configurations, exclusions);
|
||||
}
|
||||
@@ -236,41 +243,21 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
long startTime = System.nanoTime();
|
||||
String[] candidates = StringUtils.toStringArray(configurations);
|
||||
boolean skipped = false;
|
||||
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
|
||||
invokeAwareMethods(filter);
|
||||
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
|
||||
for (int i = 0; i < match.length; i++) {
|
||||
if (!match[i]) {
|
||||
candidates[i] = null;
|
||||
skipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skipped) {
|
||||
return configurations;
|
||||
}
|
||||
List<String> result = new ArrayList<>(candidates.length);
|
||||
for (String candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
int numberFiltered = configurations.size() - result.size();
|
||||
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
|
||||
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
|
||||
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
|
||||
}
|
||||
|
||||
private ConfigurationClassFilter getConfigurationClassFilter() {
|
||||
if (this.configurationClassFilter == null) {
|
||||
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
|
||||
for (AutoConfigurationImportFilter filter : filters) {
|
||||
invokeAwareMethods(filter);
|
||||
}
|
||||
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
|
||||
}
|
||||
return this.configurationClassFilter;
|
||||
}
|
||||
|
||||
protected final <T> List<T> removeDuplicates(List<T> list) {
|
||||
return new ArrayList<>(new LinkedHashSet<>(list));
|
||||
}
|
||||
@@ -354,6 +341,49 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
|
||||
private static class ConfigurationClassFilter {
|
||||
|
||||
private final AutoConfigurationMetadata autoConfigurationMetadata;
|
||||
|
||||
private final List<AutoConfigurationImportFilter> filters;
|
||||
|
||||
ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
|
||||
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
List<String> filter(List<String> configurations) {
|
||||
long startTime = System.nanoTime();
|
||||
String[] candidates = StringUtils.toStringArray(configurations);
|
||||
boolean skipped = false;
|
||||
for (AutoConfigurationImportFilter filter : this.filters) {
|
||||
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
|
||||
for (int i = 0; i < match.length; i++) {
|
||||
if (!match[i]) {
|
||||
candidates[i] = null;
|
||||
skipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skipped) {
|
||||
return configurations;
|
||||
}
|
||||
List<String> result = new ArrayList<>(candidates.length);
|
||||
for (String candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
int numberFiltered = configurations.size() - result.size();
|
||||
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
|
||||
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AutoConfigurationGroup
|
||||
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
|
||||
|
||||
@@ -391,7 +421,7 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
AutoConfigurationImportSelector.class.getSimpleName(),
|
||||
deferredImportSelector.getClass().getName()));
|
||||
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
|
||||
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
|
||||
.getAutoConfigurationEntry(annotationMetadata);
|
||||
this.autoConfigurationEntries.add(autoConfigurationEntry);
|
||||
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
|
||||
this.entries.putIfAbsent(importClassName, annotationMetadata);
|
||||
|
||||
@@ -200,6 +200,16 @@ class AutoConfigurationImportSelectorTests {
|
||||
assertThat(filter.getBeanFactory()).isEqualTo(this.beanFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getExclusionFilterReuseFilters() {
|
||||
String[] allImports = new String[] { "com.example.A", "com.example.B", "com.example.C" };
|
||||
this.filters.add(new TestAutoConfigurationImportFilter(allImports, 0));
|
||||
this.filters.add(new TestAutoConfigurationImportFilter(allImports, 2));
|
||||
assertThat(this.importSelector.getExclusionFilter().test("com.example.A")).isTrue();
|
||||
assertThat(this.importSelector.getExclusionFilter().test("com.example.B")).isFalse();
|
||||
assertThat(this.importSelector.getExclusionFilter().test("com.example.C")).isTrue();
|
||||
}
|
||||
|
||||
private String[] selectImports(Class<?> source) {
|
||||
return this.importSelector.selectImports(AnnotationMetadata.introspect(source));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user