Browse Source

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
pull/21259/head
Stephane Nicoll 6 years ago
parent
commit
6921fdacac
  1. 110
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
  2. 10
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java
  3. 6
      spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java
  4. 4
      spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java

110
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java

@ -27,6 +27,7 @@ import java.util.List; @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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);

10
spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java

@ -200,6 +200,16 @@ class AutoConfigurationImportSelectorTests { @@ -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));
}

6
spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java

@ -40,7 +40,6 @@ import javax.lang.model.SourceVersion; @@ -40,7 +40,6 @@ import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.tools.FileObject;
@ -127,10 +126,7 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { @@ -127,10 +126,7 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
TypeElement annotationType = this.processingEnv.getElementUtils().getTypeElement(annotationName);
if (annotationType != null) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {
Element enclosingElement = element.getEnclosingElement();
if (enclosingElement != null && enclosingElement.getKind() == ElementKind.PACKAGE) {
processElement(element, propertyKey, annotationName);
}
processElement(element, propertyKey, annotationName);
}
}
}

4
spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java

@ -49,14 +49,14 @@ class AutoConfigureAnnotationProcessorTests { @@ -49,14 +49,14 @@ class AutoConfigureAnnotationProcessorTests {
@Test
void annotatedClass() throws Exception {
Properties properties = compile(TestClassConfiguration.class);
assertThat(properties).hasSize(5);
assertThat(properties).hasSize(7);
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.ConditionalOnClass",
"java.io.InputStream,org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration$Nested,org.springframework.foo");
assertThat(properties).containsKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration");
assertThat(properties)
.doesNotContainKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested");
.containsKey("org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.ConditionalOnBean",
"java.io.OutputStream");

Loading…
Cancel
Save