diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java
new file mode 100644
index 00000000000..38f102038da
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012-2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.autoconfigure;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.ResourceLoaderAware;
+
+/**
+ * Filter that can be registered in {@code spring.factories} to limit the
+ * auto-configuration classes considered. This interface is designed to allow fast removal
+ * of auto-configuration classes before their bytecode is even read.
+ *
+ * An {@link AutoConfigurationImportFilter} may implement any of the following
+ * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
+ * methods will be called prior to {@link #match}:
+ *
+ * - {@link EnvironmentAware}
+ * - {@link BeanFactoryAware }
+ * - {@link BeanClassLoaderAware }
+ * - {@link ResourceLoaderAware}
+ *
+ *
+ * @author Phillip Webb
+ * @since 1.5.0
+ */
+public interface AutoConfigurationImportFilter {
+
+ /**
+ * Apply the filter to the given auto-configuration class candidates.
+ * @param autoConfigurationClasses the auto-configuration classes being considered.
+ * Implementations should not change the values in this array.
+ * @param autoConfigurationMetadata access to the meta-data generated by the
+ * auto-configure annotation processor
+ * @return a boolean array indicating which of the auto-configuration classes should
+ * be imported. The returned array must be the same size as the incoming
+ * {@code autoConfigurationClasses} parameter. Entries containing {@code false} will
+ * not be imported.
+ */
+ boolean[] match(String[] autoConfigurationClasses,
+ AutoConfigurationMetadata autoConfigurationMetadata);
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
index 034d5bf3b47..8d51e1111d4 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
@@ -24,6 +24,10 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
@@ -67,6 +71,9 @@ public class AutoConfigurationImportSelector
private static final String[] NO_IMPORTS = {};
+ private static final Log logger = LogFactory
+ .getLog(AutoConfigurationImportSelector.class);
+
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
@@ -91,6 +98,7 @@ public class AutoConfigurationImportSelector
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
+ configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportListeners(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
@@ -233,6 +241,45 @@ public class AutoConfigurationImportSelector
return configurations;
}
+ private List filter(List configurations,
+ AutoConfigurationMetadata autoConfigurationMetadata) {
+ long startTime = System.nanoTime();
+ String[] candidates = configurations.toArray(new String[configurations.size()]);
+ boolean[] skip = new boolean[candidates.length];
+ 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]) {
+ skip[i] = true;
+ skipped = true;
+ }
+ }
+ }
+ if (!skipped) {
+ return configurations;
+ }
+ List result = new ArrayList(candidates.length);
+ for (int i = 0; i < candidates.length; i++) {
+ if (!skip[i]) {
+ result.add(candidates[i]);
+ }
+ }
+ 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 new ArrayList(result);
+ }
+
+ protected List getAutoConfigurationImportFilters() {
+ return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
+ this.beanClassLoader);
+ }
+
private MetadataReaderFactory getMetadataReaderFactory() {
try {
return getBeanFactory().getBean(
@@ -271,20 +318,20 @@ public class AutoConfigurationImportSelector
this.beanClassLoader);
}
- private void invokeAwareMethods(AutoConfigurationImportListener listener) {
- if (listener instanceof Aware) {
- if (listener instanceof BeanClassLoaderAware) {
- ((BeanClassLoaderAware) listener)
+ private void invokeAwareMethods(Object instance) {
+ if (instance instanceof Aware) {
+ if (instance instanceof BeanClassLoaderAware) {
+ ((BeanClassLoaderAware) instance)
.setBeanClassLoader(this.beanClassLoader);
}
- if (listener instanceof BeanFactoryAware) {
- ((BeanFactoryAware) listener).setBeanFactory(this.beanFactory);
+ if (instance instanceof BeanFactoryAware) {
+ ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
}
- if (listener instanceof EnvironmentAware) {
- ((EnvironmentAware) listener).setEnvironment(this.environment);
+ if (instance instanceof EnvironmentAware) {
+ ((EnvironmentAware) instance).setEnvironment(this.environment);
}
- if (listener instanceof ResourceLoaderAware) {
- ((ResourceLoaderAware) listener).setResourceLoader(this.resourceLoader);
+ if (instance instanceof ResourceLoaderAware) {
+ ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
}
}
}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java
index cd703994748..9be2c9ad08f 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java
@@ -16,8 +16,11 @@
package org.springframework.boot.autoconfigure;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
@@ -25,6 +28,9 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.MockitoAnnotations;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
@@ -53,6 +59,8 @@ public class AutoConfigurationImportSelectorTests {
private final MockEnvironment environment = new MockEnvironment();
+ private List filters = new ArrayList();
+
@Rule
public ExpectedException expected = ExpectedException.none();
@@ -191,6 +199,26 @@ public class AutoConfigurationImportSelectorTests {
"org.springframework.boot.autoconfigure.DoesNotExist2");
}
+ @Test
+ public void filterShouldFilterImports() throws Exception {
+ String[] defaultImports = selectImports(BasicEnableAutoConfiguration.class);
+ this.filters.add(new TestAutoConfigurationImportFilter(defaultImports, 1));
+ this.filters.add(new TestAutoConfigurationImportFilter(defaultImports, 3, 4));
+ String[] filtered = selectImports(BasicEnableAutoConfiguration.class);
+ assertThat(filtered).hasSize(defaultImports.length - 3);
+ assertThat(filtered).doesNotContain(defaultImports[1], defaultImports[3],
+ defaultImports[4]);
+ }
+
+ @Test
+ public void filterShouldSupportAware() throws Exception {
+ TestAutoConfigurationImportFilter filter = new TestAutoConfigurationImportFilter(
+ new String[] {});
+ this.filters.add(filter);
+ selectImports(BasicEnableAutoConfiguration.class);
+ assertThat(filter.getBeanFactory()).isEqualTo(this.beanFactory);
+ }
+
private String[] selectImports(Class> source) {
return this.importSelector.selectImports(new StandardAnnotationMetadata(source));
}
@@ -200,11 +228,16 @@ public class AutoConfigurationImportSelectorTests {
getClass().getClassLoader());
}
- private static class TestAutoConfigurationImportSelector
+ private class TestAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
private AutoConfigurationImportEvent lastEvent;
+ @Override
+ protected List getAutoConfigurationImportFilters() {
+ return AutoConfigurationImportSelectorTests.this.filters;
+ }
+
@Override
protected List getAutoConfigurationImportListeners() {
return Collections.singletonList(
@@ -225,6 +258,40 @@ public class AutoConfigurationImportSelectorTests {
}
+ private static class TestAutoConfigurationImportFilter
+ implements AutoConfigurationImportFilter, BeanFactoryAware {
+
+ private final Set nonMatching = new HashSet();
+
+ private BeanFactory beanFactory;
+
+ TestAutoConfigurationImportFilter(String[] configurations, int... nonMatching) {
+ for (int i : nonMatching) {
+ this.nonMatching.add(configurations[i]);
+ }
+ }
+
+ @Override
+ public boolean[] match(String[] autoConfigurationClasses,
+ AutoConfigurationMetadata autoConfigurationMetadata) {
+ boolean[] result = new boolean[autoConfigurationClasses.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = !this.nonMatching.contains(autoConfigurationClasses[i]);
+ }
+ return result;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
+ public BeanFactory getBeanFactory() {
+ return this.beanFactory;
+ }
+
+ }
+
@Configuration
private class TestConfiguration {