Add AutoConfigurationImportFilter support
Add `AutoConfigurationImportFilter` strategy interface which can be used to filter auto-configuration candidates before they are loaded. See gh-7573
This commit is contained in:
parent
02641a8207
commit
20a20b7711
|
@ -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.
|
||||
* <p>
|
||||
* 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}:
|
||||
* <ul>
|
||||
* <li>{@link EnvironmentAware}</li>
|
||||
* <li>{@link BeanFactoryAware }</li>
|
||||
* <li>{@link BeanClassLoaderAware }</li>
|
||||
* <li>{@link ResourceLoaderAware}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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);
|
||||
|
||||
}
|
|
@ -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<String> 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<String> filter(List<String> 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<String> result = new ArrayList<String>(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<String>(result);
|
||||
}
|
||||
|
||||
protected List<AutoConfigurationImportFilter> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<AutoConfigurationImportFilter> filters = new ArrayList<AutoConfigurationImportFilter>();
|
||||
|
||||
@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<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
|
||||
return AutoConfigurationImportSelectorTests.this.filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
|
||||
return Collections.<AutoConfigurationImportListener>singletonList(
|
||||
|
@ -225,6 +258,40 @@ public class AutoConfigurationImportSelectorTests {
|
|||
|
||||
}
|
||||
|
||||
private static class TestAutoConfigurationImportFilter
|
||||
implements AutoConfigurationImportFilter, BeanFactoryAware {
|
||||
|
||||
private final Set<String> nonMatching = new HashSet<String>();
|
||||
|
||||
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 {
|
||||
|
||||
|
|
Loading…
Reference in New Issue