Add `include` support to @EnableAutoConfiguration

Update the `@EnableAutoConfiguration` annotation to include an `include`
attribute which can be used to specify specific auto-configuration
classes.

Primarily added to so that tests can selectively auto-configure without
needing to worry about class import order.

Fixes gh-3660
This commit is contained in:
Phillip Webb 2015-08-04 16:43:05 -07:00
parent a10bfc153d
commit 1d31d23e29
4 changed files with 48 additions and 7 deletions

View File

@ -75,6 +75,15 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
AutoConfigurationPackages.Registrar.class }) AutoConfigurationPackages.Registrar.class })
public @interface EnableAutoConfiguration { public @interface EnableAutoConfiguration {
/**
* Include only the specified auto-configuration classes and do not attempt full
* auto-configuration. Using this attribute means that {@code spring.factories} files
* will not be considered. This attribute should not generally be specified in
* production applications, however, it is useful for tests.
* @return the classes to include
*/
Class<?>[] include() default {};
/** /**
* Exclude specific auto-configuration classes such that they will never be applied. * Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude * @return the classes to exclude

View File

@ -75,14 +75,12 @@ class EnableAutoConfigurationImportSelector implements DeferredImportSelector,
+ " annotated with @EnableAutoConfiguration?"); + " annotated with @EnableAutoConfiguration?");
// Find all possible auto configuration classes, filtering duplicates // Find all possible auto configuration classes, filtering duplicates
List<String> factories = new ArrayList<String>(new LinkedHashSet<String>( List<String> factories = getFactories(attributes);
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
this.beanClassLoader)));
// Remove those specifically excluded // Remove those specifically excluded
Set<String> excluded = new LinkedHashSet<String>(); Set<String> excluded = new LinkedHashSet<String>();
excluded.addAll(Arrays.asList(attributes.getStringArray("exclude"))); excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); excluded.addAll(asList(attributes, "excludeName"));
excluded.addAll(getExcludeAutoConfigurationsProperty()); excluded.addAll(getExcludeAutoConfigurationsProperty());
factories.removeAll(excluded); factories.removeAll(excluded);
ConditionEvaluationReport.get(this.beanFactory).recordExclusions(excluded); ConditionEvaluationReport.get(this.beanFactory).recordExclusions(excluded);
@ -100,6 +98,20 @@ class EnableAutoConfigurationImportSelector implements DeferredImportSelector,
} }
} }
private List<String> getFactories(AnnotationAttributes attributes) {
List<String> factories = asList(attributes, "include");
if (factories.isEmpty()) {
factories = SpringFactoriesLoader.loadFactoryNames(
EnableAutoConfiguration.class, this.beanClassLoader);
}
return new ArrayList<String>(new LinkedHashSet<String>(factories));
}
private List<String> asList(AnnotationAttributes attributes, String name) {
String[] value = attributes.getStringArray(name);
return Arrays.asList(value == null ? new String[0] : value);
}
private List<String> getExcludeAutoConfigurationsProperty() { private List<String> getExcludeAutoConfigurationsProperty() {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(this.environment, RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(this.environment,
"spring.autoconfigure."); "spring.autoconfigure.");

View File

@ -148,6 +148,18 @@ public class EnableAutoConfigurationImportSelectorTests {
ThymeleafAutoConfiguration.class.getName())); ThymeleafAutoConfiguration.class.getName()));
} }
@Test
public void classIncludesAreApplied() throws Exception {
given(
this.annotationMetadata.getAnnotationAttributes(
EnableAutoConfiguration.class.getName(), true)).willReturn(
this.annotationAttributes);
given(this.annotationAttributes.getStringArray("include")).willReturn(
new String[] { FreeMarkerAutoConfiguration.class.getName() });
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
assertThat(imports.length, is(equalTo(1)));
}
private void configureExclusions(String[] classExclusion, String[] nameExclusion, private void configureExclusions(String[] classExclusion, String[] nameExclusion,
String[] propertyExclusion) { String[] propertyExclusion) {
given( given(
@ -168,4 +180,5 @@ public class EnableAutoConfigurationImportSelectorTests {
return SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, return SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
getClass().getClassLoader()); getClass().getClassLoader());
} }
} }

View File

@ -18,9 +18,11 @@ package org.springframework.boot.autoconfigure.context;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import static org.hamcrest.core.Is.is; import static org.hamcrest.core.Is.is;
@ -44,8 +46,7 @@ public class ConfigurationPropertiesAutoConfigurationTests {
@Test @Test
public void processAnnotatedBean() { public void processAnnotatedBean() {
load(new Class[] { SampleBean.class, load(new Class[] { AutoConfigured.class, SampleBean.class }, "foo.name:test");
ConfigurationPropertiesAutoConfiguration.class }, "foo.name:test");
assertThat(this.context.getBean(SampleBean.class).getName(), is("test")); assertThat(this.context.getBean(SampleBean.class).getName(), is("test"));
} }
@ -62,6 +63,12 @@ public class ConfigurationPropertiesAutoConfigurationTests {
this.context.refresh(); this.context.refresh();
} }
@Configuration
@EnableAutoConfiguration(include = ConfigurationPropertiesAutoConfiguration.class)
static class AutoConfigured {
}
@Component @Component
@ConfigurationProperties("foo") @ConfigurationProperties("foo")
static class SampleBean { static class SampleBean {