From eb5d92f3f06403dc0b0c7d545ac754837e4fd52e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 3 Aug 2015 15:55:05 +0200 Subject: [PATCH] Exclude auto-configurations via property Add `spring.autoconfigure.exclude` to control the list of auto-configuration classes to exclude via configuration. Merge the exclusions defined on the `@EnableAutoConfiguration` or `@SpringBooApplication` if any. Closes gh-2435 --- .../EnableAutoConfiguration.java | 5 +- ...EnableAutoConfigurationImportSelector.java | 24 ++++++++- ...itional-spring-configuration-metadata.json | 5 ++ ...eAutoConfigurationImportSelectorTests.java | 54 ++++++++++++++++--- .../appendix-application-properties.adoc | 3 ++ .../src/main/asciidoc/using-spring-boot.adoc | 7 ++- 6 files changed, 87 insertions(+), 11 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfiguration.java index d7837380f42..dca59dca18e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfiguration.java @@ -44,8 +44,9 @@ import org.springframework.core.io.support.SpringFactoriesLoader; * Auto-configuration tries to be as intelligent as possible and will back-away as you * define more of your own configuration. You can always manually {@link #exclude()} any * configuration that you never want to apply (use {@link #excludeName()} if you don't - * have access to them). Auto-configuration is always applied after user-defined beans - * have been registered. + * have access to them). You can also exclude them via the + * {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied + * after user-defined beans have been registered. *

* The package of the class that is annotated with {@code @EnableAutoConfiguration} has * specific significance and is often used as a 'default'. For example, it will be used diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelector.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelector.java index 88173e63c17..d07d36cb2d8 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelector.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelector.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -29,11 +30,14 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.DeferredImportSelector; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.type.AnnotationMetadata; @@ -50,10 +54,12 @@ import org.springframework.util.Assert; */ @Order(Ordered.LOWEST_PRECEDENCE - 1) class EnableAutoConfigurationImportSelector implements DeferredImportSelector, - BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware { + BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware { private ConfigurableListableBeanFactory beanFactory; + private Environment environment; + private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; @@ -78,6 +84,7 @@ class EnableAutoConfigurationImportSelector implements DeferredImportSelector, Set excluded = new LinkedHashSet(); excluded.addAll(Arrays.asList(attributes.getStringArray("exclude"))); excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); + excluded.addAll(getExcludeAutoConfigurationsProperty()); factories.removeAll(excluded); ConditionEvaluationReport.get(this.beanFactory).recordExclusions(excluded); ConditionEvaluationReport.get(this.beanFactory).recordEvaluationCandidates( @@ -94,6 +101,16 @@ class EnableAutoConfigurationImportSelector implements DeferredImportSelector, } } + private List getExcludeAutoConfigurationsProperty() { + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(this.environment, + "spring.autoconfigure."); + String[] value = resolver.getProperty("exclude", String[].class); + if (value != null) { + return Arrays.asList(value); + } + return Collections.emptyList(); + } + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; @@ -110,4 +127,9 @@ class EnableAutoConfigurationImportSelector implements DeferredImportSelector, this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + } diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 937ad11bb59..88d30bc4a59 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -29,6 +29,11 @@ "description": "JMX name of the application admin MBean.", "defaultValue": "org.springframework.boot:type=Admin,name=SpringApplication" }, + { + "name": "spring.autoconfigure.exclude", + "type": "java.lang.Class[]", + "description": "Auto-configuration classes to exclude." + }, { "name": "spring.batch.job.enabled", "type": "java.lang.Boolean", diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelectorTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelectorTests.java index 01ce4c9c822..ac55ddb942a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelectorTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/EnableAutoConfigurationImportSelectorTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure; import java.util.List; +import org.flywaydb.core.internal.util.StringUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,11 +28,13 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.mock.env.MockEnvironment; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -54,6 +57,8 @@ public class EnableAutoConfigurationImportSelectorTests { private final ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + private final MockEnvironment environment = new MockEnvironment(); + @Mock private AnnotationMetadata annotationMetadata; @@ -63,12 +68,13 @@ public class EnableAutoConfigurationImportSelectorTests { @Before public void configureImportSelector() { this.importSelector.setBeanFactory(this.beanFactory); + this.importSelector.setEnvironment(this.environment); this.importSelector.setResourceLoader(new DefaultResourceLoader()); } @Test public void importsAreSelected() { - configureExclusions(new String[0], new String[0]); + configureExclusions(new String[0], new String[0], new String[0]); String[] imports = this.importSelector.selectImports(this.annotationMetadata); assertThat( imports.length, @@ -82,7 +88,7 @@ public class EnableAutoConfigurationImportSelectorTests { @Test public void classExclusionsAreApplied() { configureExclusions(new String[] { FreeMarkerAutoConfiguration.class.getName() }, - new String[0]); + new String[0], new String[0]); String[] imports = this.importSelector.selectImports(this.annotationMetadata); assertThat(imports.length, is(equalTo(getAutoConfigurationClassNames().size() - 1))); @@ -93,7 +99,7 @@ public class EnableAutoConfigurationImportSelectorTests { @Test public void classNamesExclusionsAreApplied() { configureExclusions(new String[0], - new String[] { VelocityAutoConfiguration.class.getName() }); + new String[] { VelocityAutoConfiguration.class.getName() }, new String[0]); String[] imports = this.importSelector.selectImports(this.annotationMetadata); assertThat(imports.length, is(equalTo(getAutoConfigurationClassNames().size() - 1))); @@ -102,9 +108,22 @@ public class EnableAutoConfigurationImportSelectorTests { } @Test - public void bothExclusionsAreApplied() { - configureExclusions(new String[] { VelocityAutoConfiguration.class.getName() }, - new String[] { FreeMarkerAutoConfiguration.class.getName() }); + public void propertyExclusionsAreApplied() { + configureExclusions(new String[0], new String[0], new String[] { + FreeMarkerAutoConfiguration.class.getName()}); + String[] imports = this.importSelector.selectImports(this.annotationMetadata); + assertThat(imports.length, + is(equalTo(getAutoConfigurationClassNames().size() - 1))); + assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions(), + contains(FreeMarkerAutoConfiguration.class.getName())); + } + + @Test + public void severalPropertyExclusionsAreApplied() { + configureExclusions(new String[0], new String[0], new String[] { + FreeMarkerAutoConfiguration.class.getName(), VelocityAutoConfiguration + .class.getName()}); + String[] imports = this.importSelector.selectImports(this.annotationMetadata); assertThat(imports.length, is(equalTo(getAutoConfigurationClassNames().size() - 2))); @@ -114,7 +133,23 @@ public class EnableAutoConfigurationImportSelectorTests { VelocityAutoConfiguration.class.getName())); } - private void configureExclusions(String[] classExclusion, String[] nameExclusion) { + @Test + public void combinedExclusionsAreApplied() { + configureExclusions(new String[] { VelocityAutoConfiguration.class.getName() }, + new String[] { FreeMarkerAutoConfiguration.class.getName() }, + new String[] { ThymeleafAutoConfiguration.class.getName() }); + String[] imports = this.importSelector.selectImports(this.annotationMetadata); + assertThat(imports.length, + is(equalTo(getAutoConfigurationClassNames().size() - 3))); + assertThat( + ConditionEvaluationReport.get(this.beanFactory).getExclusions(), + containsInAnyOrder(FreeMarkerAutoConfiguration.class.getName(), + VelocityAutoConfiguration.class.getName(), + ThymeleafAutoConfiguration.class.getName())); + } + + private void configureExclusions(String[] classExclusion, String[] nameExclusion, + String[] propertyExclusion) { given( this.annotationMetadata.getAnnotationAttributes( EnableAutoConfiguration.class.getName(), true)).willReturn( @@ -123,6 +158,11 @@ public class EnableAutoConfigurationImportSelectorTests { classExclusion); given(this.annotationAttributes.getStringArray("excludeName")).willReturn( nameExclusion); + if (propertyExclusion.length > 0) { + String value = StringUtils.arrayToCommaDelimitedString(propertyExclusion); + this.environment.setProperty("spring.autoconfigure.exclude", + value); + } } private List getAutoConfigurationClassNames() { diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 89c84312010..4a37ac3cc8f 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -45,6 +45,9 @@ content into your application; rather pick only the properties that you need. spring.main.show-banner=true spring.main....= # see class for all properties + # AUTO-CONFIGURATION + spring.autoconfigure.exclude= # comma-separated list of auto-configuration classes to exclude + # ADMIN ({sc-spring-boot-autoconfigure}/admin/SpringApplicationAdminJmxAutoConfiguration.{sc-ext}[SpringApplicationAdminJmxAutoConfiguration]) spring.application.admin.enabled=false # enable admin features for the application spring.application.admin.jmx-name=org.springframework.boot:type=Admin,name=SpringApplication # JMX name of the application admin MBean diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc index 54c194b765a..5a7c556b3cd 100644 --- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc +++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc @@ -602,7 +602,7 @@ report to the console. -[[using-boot-disabling-specific-auto-configutation]] +[[using-boot-disabling-specific-auto-configuration]] === Disabling specific auto-configuration If you find that specific auto-configure classes are being applied that you don't want, you can use the exclude attribute of `@EnableAutoConfiguration` to disable them. @@ -619,7 +619,12 @@ you can use the exclude attribute of `@EnableAutoConfiguration` to disable them. } ---- +If the class is not on the classpath, you can use the `excludeName` attribute of +the annotation and specify the fully qualified name instead. Finally, you can also +control the list of auto-configuration classes to excludes via the +`spring.autoconfigure.exclude` property. +TIP: You can define exclusions both at the annotation level and using the property. [[using-boot-spring-beans-and-dependency-injection]] == Spring Beans and dependency injection