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 7a555528ac..809631fe81 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
@@ -39,7 +39,6 @@ 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.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
@@ -58,12 +57,12 @@ import org.springframework.util.ClassUtils;
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
- * @see EnableAutoConfiguration
* @since 1.3.0
+ * @see EnableAutoConfiguration
*/
-@Order(Ordered.LOWEST_PRECEDENCE - 1)
-public class EnableAutoConfigurationImportSelector implements DeferredImportSelector,
- BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {
+public class EnableAutoConfigurationImportSelector
+ implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
+ BeanFactoryAware, EnvironmentAware, Ordered {
private ConfigurableListableBeanFactory beanFactory;
@@ -242,6 +241,11 @@ public class EnableAutoConfigurationImportSelector implements DeferredImportSele
return this.resourceLoader;
}
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE - 1;
+ }
+
/**
* Bindable object used to get excludes.
*/
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfiguration.java
new file mode 100644
index 0000000000..463d234bca
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfiguration.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012-2016 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 java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+/**
+ * Import and apply the selected auto-configuration classes. Applies the same ordering
+ * rules as {@code @EnableAutoConfiguration} but restricts the auto-configuration classes
+ * to the specified set, rather than consulting {@code spring.factories}.
+ *
+ * Generally, {@code @EnableAutoConfiguration} should used in preference to this
+ * annotation, however, {@code @ImportAutoConfiguration} can be useful in some situations
+ * and especially when writing tests.
+ *
+ * @author Phillip Webb
+ * @since 1.3.0
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@AutoConfigurationPackage
+@Import(ImportAutoConfigurationImportSelector.class)
+public @interface ImportAutoConfiguration {
+
+ /**
+ * The auto-configuration classes that should be imported.
+ * @return the classes to import
+ */
+ Class>[] value();
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java
new file mode 100644
index 0000000000..b797968a16
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelector.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2016 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 java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Variant of {@link EnableAutoConfigurationImportSelector} for
+ * {@link ImportAutoConfiguration}.
+ *
+ * @author Phillip Webb
+ */
+class ImportAutoConfigurationImportSelector
+ extends EnableAutoConfigurationImportSelector {
+
+ private static final Set ANNOTATION_NAMES;
+
+ static {
+ Set names = new LinkedHashSet();
+ names.add(ImportAutoConfiguration.class.getName());
+ names.add("org.springframework.boot.autoconfigure.test.ImportAutoConfiguration");
+ ANNOTATION_NAMES = Collections.unmodifiableSet(names);
+ }
+
+ @Override
+ protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
+ return null;
+ }
+
+ @Override
+ protected List getCandidateConfigurations(AnnotationMetadata metadata,
+ AnnotationAttributes attributes) {
+ try {
+ return getCandidateConfigurations(
+ ClassUtils.forName(metadata.getClassName(), null));
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ private List getCandidateConfigurations(Class> source) {
+ Set candidates = new LinkedHashSet();
+ collectCandidateConfigurations(source, candidates);
+ return new ArrayList(candidates);
+ }
+
+ private void collectCandidateConfigurations(Class> source, Set candidates) {
+ if (source != null) {
+ for (Annotation annotation : source.getDeclaredAnnotations()) {
+ if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
+ collectCandidateConfigurations(annotation, candidates);
+ }
+ }
+ collectCandidateConfigurations(source.getSuperclass(), candidates);
+ }
+ }
+
+ private void collectCandidateConfigurations(Annotation annotation,
+ Set candidates) {
+ if (ANNOTATION_NAMES.contains(annotation.annotationType().getName())) {
+ String[] value = (String[]) AnnotationUtils
+ .getAnnotationAttributes(annotation, true).get("value");
+ candidates.addAll(Arrays.asList(value));
+ }
+ collectCandidateConfigurations(annotation.annotationType(), candidates);
+ }
+
+ @Override
+ protected Set getExclusions(AnnotationMetadata metadata,
+ AnnotationAttributes attributes) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public int getOrder() {
+ return super.getOrder() - 1;
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfiguration.java
index 7b395fe908..0241205be1 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfiguration.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -23,8 +23,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
-import org.springframework.context.annotation.Import;
+import org.springframework.core.annotation.AliasFor;
/**
* Import and apply the selected auto-configuration classes for testing purposes. Applies
@@ -34,19 +33,22 @@ import org.springframework.context.annotation.Import;
*
* @author Phillip Webb
* @since 1.3.0
+ * @deprecated since 1.4.0 in favor of
+ * {@link org.springframework.boot.autoconfigure.ImportAutoConfiguration}
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@AutoConfigurationPackage
-@Import(ImportAutoConfigurationImportSelector.class)
+@org.springframework.boot.autoconfigure.ImportAutoConfiguration({})
+@Deprecated
public @interface ImportAutoConfiguration {
/**
* The auto-configuration classes that should be imported.
* @return the classes to import
*/
+ @AliasFor(annotation = org.springframework.boot.autoconfigure.ImportAutoConfiguration.class, attribute = "value")
Class>[] value();
}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfigurationImportSelector.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfigurationImportSelector.java
deleted file mode 100644
index 3a46aaa04d..0000000000
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/ImportAutoConfigurationImportSelector.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2012-2015 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.test;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.boot.autoconfigure.EnableAutoConfigurationImportSelector;
-import org.springframework.core.annotation.AnnotationAttributes;
-import org.springframework.core.type.AnnotationMetadata;
-
-/**
- * Variant of {@link EnableAutoConfigurationImportSelector} for
- * {@link ImportAutoConfiguration}.
- *
- * @author Phillip Webb
- */
-class ImportAutoConfigurationImportSelector
- extends EnableAutoConfigurationImportSelector {
-
- @Override
- protected Class> getAnnotationClass() {
- return ImportAutoConfiguration.class;
- }
-
- @Override
- protected Set getExclusions(AnnotationMetadata metadata,
- AnnotationAttributes attributes) {
- return Collections.emptySet();
- }
-
- @Override
- protected List getCandidateConfigurations(AnnotationMetadata metadata,
- AnnotationAttributes attributes) {
- return asList(attributes, "value");
- }
-
-}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/package-info.java
index 1f2eb910ae..0bc67cf9d8 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/package-info.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/test/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -16,5 +16,6 @@
/**
* Test utilities related to auto-configuration.
+ * @deprecated in 1.4.0 in favor of the {@code spring-test-autoconfigure} module
*/
package org.springframework.boot.autoconfigure.test;
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/test/ImportAutoConfigurationImportSelectorTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelectorTests.java
similarity index 53%
rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/test/ImportAutoConfigurationImportSelectorTests.java
rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelectorTests.java
index 4e8f4ea128..3fd9aa5c16 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/test/ImportAutoConfigurationImportSelectorTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationImportSelectorTests.java
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-package org.springframework.boot.autoconfigure.test;
+package org.springframework.boot.autoconfigure;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import org.junit.Before;
import org.junit.Test;
@@ -25,13 +28,13 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
-import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
@@ -49,12 +52,6 @@ public class ImportAutoConfigurationImportSelectorTests {
@Mock
private Environment environment;
- @Mock
- private AnnotationMetadata annotationMetadata;
-
- @Mock
- private AnnotationAttributes annotationAttributes;
-
@Before
public void configureImportSelector() {
this.importSelector.setBeanFactory(this.beanFactory);
@@ -63,25 +60,54 @@ public class ImportAutoConfigurationImportSelectorTests {
}
@Test
- public void importsAreSelected() {
- String[] value = new String[] { FreeMarkerAutoConfiguration.class.getName() };
- configureValue(value);
- String[] imports = this.importSelector.selectImports(this.annotationMetadata);
- assertThat(imports).isEqualTo(value);
+ public void importsAreSelected() throws Exception {
+ AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
+ .getMetadataReader(ImportFreemarker.class.getName())
+ .getAnnotationMetadata();
+ String[] imports = this.importSelector.selectImports(annotationMetadata);
+ assertThat(imports).containsExactly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
- public void propertyExclusionsAreNotApplied() {
- configureValue(new String[] { FreeMarkerAutoConfiguration.class.getName() });
- this.importSelector.selectImports(this.annotationMetadata);
+ public void propertyExclusionsAreNotApplied() throws Exception {
+ AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
+ .getMetadataReader(ImportFreemarker.class.getName())
+ .getAnnotationMetadata();
+ this.importSelector.selectImports(annotationMetadata);
verifyZeroInteractions(this.environment);
}
- private void configureValue(String... value) {
- String name = ImportAutoConfiguration.class.getName();
- given(this.annotationMetadata.getAnnotationAttributes(name, true))
- .willReturn(this.annotationAttributes);
- given(this.annotationAttributes.getStringArray("value")).willReturn(value);
+ @Test
+ public void multipleImportsAreFound() throws Exception {
+ AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
+ .getMetadataReader(MultipleImports.class.getName())
+ .getAnnotationMetadata();
+ String[] imports = this.importSelector.selectImports(annotationMetadata);
+ assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName(),
+ ThymeleafAutoConfiguration.class.getName());
+ }
+
+ @ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
+ static class ImportFreemarker {
+
+ }
+
+ @ImportOne
+ @ImportTwo
+ static class MultipleImports {
+
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
+ static @interface ImportOne {
+
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @ImportAutoConfiguration(ThymeleafAutoConfiguration.class)
+ static @interface ImportTwo {
+
}
}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationTests.java
new file mode 100644
index 0000000000..29df53aeaa
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ImportAutoConfigurationTests.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012-2016 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ClassUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ImportAutoConfigurationTests}.
+ *
+ * @author Phillip Webb
+ */
+public class ImportAutoConfigurationTests {
+
+ @Test
+ public void multipleAnnotationsShouldMergeCorrectly() {
+ testConfigImports(Config.class);
+ testConfigImports(AnotherConfig.class);
+ }
+
+ private void testConfigImports(Class> config) {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
+ config);
+ String shortName = ClassUtils.getShortName(ImportAutoConfigurationTests.class);
+ int beginIndex = shortName.length() + 1;
+ List orderdConfigBeans = new ArrayList();
+ for (String bean : context.getBeanDefinitionNames()) {
+ if (bean.contains("$Config")) {
+ String shortBeanName = ClassUtils.getShortName(bean);
+ orderdConfigBeans.add(shortBeanName.substring(beginIndex));
+ }
+ }
+ assertThat(orderdConfigBeans).containsExactly("ConfigA", "ConfigB", "ConfigC",
+ "ConfigD");
+ context.close();
+ }
+
+ @ImportAutoConfiguration({ ConfigD.class, ConfigB.class })
+ @MetaImportAutoConfiguration
+ static class Config {
+
+ }
+
+ @MetaImportAutoConfiguration
+ @ImportAutoConfiguration({ ConfigB.class, ConfigD.class })
+ static class AnotherConfig {
+
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @ImportAutoConfiguration({ ConfigC.class, ConfigA.class })
+ @interface MetaImportAutoConfiguration {
+
+ }
+
+ @Configuration
+ static class ConfigA {
+ }
+
+ @Configuration
+ @AutoConfigureAfter(ConfigA.class)
+ static class ConfigB {
+ }
+
+ @Configuration
+ @AutoConfigureAfter(ConfigB.class)
+ static class ConfigC {
+ }
+
+ @Configuration
+ @AutoConfigureAfter(ConfigC.class)
+ static class ConfigD {
+ }
+
+}