Merge branch '1.5.x'

This commit is contained in:
Phillip Webb
2017-01-27 17:50:01 -08:00
19 changed files with 857 additions and 130 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* 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.
@@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -31,6 +32,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.annotation.DeterminableImports;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@@ -122,12 +124,52 @@ public abstract class AutoConfigurationPackages {
* configuration.
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
static class Registrar implements ImportBeanDefinitionRegistrar {
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, ClassUtils.getPackageName(metadata.getClassName()));
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.<Object>singleton(new PackageImport(metadata));
}
}
/**
* Wrapper for a package import.
*/
private final static class PackageImport {
private final String packageName;
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
@Override
public int hashCode() {
return this.packageName.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return this.packageName.equals(((PackageImport) obj).packageName);
}
public String getPackageName() {
return this.packageName;
}
@Override
public String toString() {
return "Package Import " + this.packageName;
}
}

View File

@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.boot.context.annotation.DeterminableImports;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
@@ -44,7 +45,8 @@ import org.springframework.util.ObjectUtils;
* @author Phillip Webb
* @author Andy Wilkinson
*/
class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelector
implements DeterminableImports {
private static final Set<String> ANNOTATION_NAMES;
@@ -55,6 +57,14 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec
ANNOTATION_NAMES = Collections.unmodifiableSet(names);
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
Set<String> result = new LinkedHashSet<String>();
result.addAll(getCandidateConfigurations(metadata, null));
result.removeAll(getExclusions(metadata, null));
return Collections.<Object>unmodifiableSet(result);
}
@Override
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
return null;
@@ -85,6 +95,10 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec
if (classes.length > 0) {
return Arrays.asList(classes);
}
return loadFactoryNames(source);
}
protected Collection<String> loadFactoryNames(Class<?> source) {
return SpringFactoriesLoader.loadFactoryNames(source,
getClass().getClassLoader());
}
@@ -117,7 +131,8 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec
return exclusions;
}
private Map<Class<?>, List<Annotation>> getAnnotations(AnnotationMetadata metadata) {
protected final Map<Class<?>, List<Annotation>> getAnnotations(
AnnotationMetadata metadata) {
MultiValueMap<Class<?>, Annotation> annotations = new LinkedMultiValueMap<Class<?>, Annotation>();
Class<?> source = ClassUtils.resolveClassName(metadata.getClassName(), null);
collectAnnotations(source, annotations, new HashSet<Class<?>>());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* 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.
@@ -19,6 +19,9 @@ package org.springframework.boot.autoconfigure;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +49,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
*/
public class ImportAutoConfigurationImportSelectorTests {
private final ImportAutoConfigurationImportSelector importSelector = new ImportAutoConfigurationImportSelector();
private final ImportAutoConfigurationImportSelector importSelector = new TestImportAutoConfigurationImportSelector();
private final ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@@ -63,36 +66,32 @@ public class ImportAutoConfigurationImportSelectorTests {
@Test
public void importsAreSelected() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(ImportFreeMarker.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportFreeMarker.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsExactly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void importsAreSelectedUsingClassesAttribute() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(ImportFreeMarkerUsingClassesAttribute.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportFreeMarkerUsingClassesAttribute.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsExactly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void propertyExclusionsAreNotApplied() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(ImportFreeMarker.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportFreeMarker.class);
this.importSelector.selectImports(annotationMetadata);
verifyZeroInteractions(this.environment);
}
@Test
public void multipleImportsAreFound() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(MultipleImports.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
MultipleImports.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName(),
ThymeleafAutoConfiguration.class.getName());
@@ -100,41 +99,94 @@ public class ImportAutoConfigurationImportSelectorTests {
@Test
public void selfAnnotatingAnnotationDoesNotCauseStackOverflow() throws IOException {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(ImportWithSelfAnnotatingAnnotation.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportWithSelfAnnotatingAnnotation.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(ThymeleafAutoConfiguration.class.getName());
}
@Test
public void exclusionsAreApplied() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(MultipleImportsWithExclusion.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
MultipleImportsWithExclusion.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void exclusionsWithoutImport() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(ExclusionWithoutImport.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ExclusionWithoutImport.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsOnly(FreeMarkerAutoConfiguration.class.getName());
}
@Test
public void exclusionsAliasesAreApplied() throws Exception {
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory()
.getMetadataReader(
ImportWithSelfAnnotatingAnnotationExclude.class.getName())
.getAnnotationMetadata();
AnnotationMetadata annotationMetadata = getAnnotationMetadata(
ImportWithSelfAnnotatingAnnotationExclude.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).isEmpty();
}
@Test
public void determineImportsWhenUsingMetaWithoutClassesShouldBeEqual()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(
getAnnotationMetadata(ImportMetaAutoConfigurationWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportMetaAutoConfigurationWithUnrelatedTwo.class));
assertThat(set1).isEqualTo(set2);
assertThat(set1.hashCode()).isEqualTo(set2.hashCode());
}
@Test
public void determineImportsWhenUsingNonMetaWithoutClassesShouldBeSame()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithUnrelatedTwo.class));
assertThat(set1).isEqualTo(set2);
}
@Test
public void determineImportsWhenUsingNonMetaWithClassesShouldBeSame()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithItemsOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportAutoConfigurationWithItemsOne.class));
assertThat(set1).isEqualTo(set2);
}
@Test
public void determineImportsWhenUsingMetaExcludeWithoutClassesShouldBeEqual()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(getAnnotationMetadata(
ImportMetaAutoConfigurationExcludeWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(getAnnotationMetadata(
ImportMetaAutoConfigurationExcludeWithUnrelatedTwo.class));
assertThat(set1).isEqualTo(set2);
assertThat(set1.hashCode()).isEqualTo(set2.hashCode());
}
@Test
public void determineImportsWhenUsingMetaDifferentExcludeWithoutClassesShouldBeDifferent()
throws Exception {
Set<Object> set1 = this.importSelector.determineImports(getAnnotationMetadata(
ImportMetaAutoConfigurationExcludeWithUnrelatedOne.class));
Set<Object> set2 = this.importSelector.determineImports(
getAnnotationMetadata(ImportMetaAutoConfigurationWithUnrelatedTwo.class));
assertThat(set1).isNotEqualTo(set2);
}
private AnnotationMetadata getAnnotationMetadata(Class<?> source) throws IOException {
return new SimpleMetadataReaderFactory().getMetadataReader(source.getName())
.getAnnotationMetadata();
}
@ImportAutoConfiguration(FreeMarkerAutoConfiguration.class)
static class ImportFreeMarker {
@@ -186,6 +238,73 @@ public class ImportAutoConfigurationImportSelectorTests {
}
@MetaImportAutoConfiguration
@UnrelatedOne
static class ImportMetaAutoConfigurationWithUnrelatedOne {
}
@MetaImportAutoConfiguration
@UnrelatedTwo
static class ImportMetaAutoConfigurationWithUnrelatedTwo {
}
@ImportAutoConfiguration
@UnrelatedOne
static class ImportAutoConfigurationWithUnrelatedOne {
}
@ImportAutoConfiguration
@UnrelatedTwo
static class ImportAutoConfigurationWithUnrelatedTwo {
}
@ImportAutoConfiguration(classes = ThymeleafAutoConfiguration.class)
@UnrelatedOne
static class ImportAutoConfigurationWithItemsOne {
}
@ImportAutoConfiguration(classes = ThymeleafAutoConfiguration.class)
@UnrelatedOne
static class ImportAutoConfigurationWithItemsTwo {
}
@MetaImportAutoConfiguration(exclude = ThymeleafAutoConfiguration.class)
@UnrelatedOne
static class ImportMetaAutoConfigurationExcludeWithUnrelatedOne {
}
@MetaImportAutoConfiguration(exclude = ThymeleafAutoConfiguration.class)
@UnrelatedTwo
static class ImportMetaAutoConfigurationExcludeWithUnrelatedTwo {
}
@ImportAutoConfiguration
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaImportAutoConfiguration {
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};
}
@Retention(RetentionPolicy.RUNTIME)
static @interface UnrelatedOne {
}
@Retention(RetentionPolicy.RUNTIME)
static @interface UnrelatedTwo {
}
@Retention(RetentionPolicy.RUNTIME)
@ImportAutoConfiguration(ThymeleafAutoConfiguration.class)
@SelfAnnotating
@@ -196,4 +315,18 @@ public class ImportAutoConfigurationImportSelectorTests {
}
private static class TestImportAutoConfigurationImportSelector
extends ImportAutoConfigurationImportSelector {
@Override
protected Collection<String> loadFactoryNames(Class<?> source) {
if (source == MetaImportAutoConfiguration.class) {
return Arrays.asList(ThymeleafAutoConfiguration.class.getName(),
FreeMarkerAutoConfiguration.class.getName());
}
return super.loadFactoryNames(source);
}
}
}