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,8 +18,10 @@ package org.springframework.boot.test.context;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.BeansException;
@@ -30,19 +32,24 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.context.annotation.DeterminableImports;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.style.ToStringCreator;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.ReflectionUtils;
/**
* {@link ContextCustomizer} to allow {@code @Import} annotations to be used directly on
@@ -127,6 +134,11 @@ class ImportsContextCustomizer implements ContextCustomizer {
return this.key.equals(other.key);
}
@Override
public String toString() {
return new ToStringCreator(this).append("key", this.key).toString();
}
/**
* {@link Configuration} registered to trigger the {@link ImportsSelector}.
*/
@@ -214,6 +226,8 @@ class ImportsContextCustomizer implements ContextCustomizer {
*/
static class ContextCustomizerKey {
private static final Class<?>[] NO_IMPORTS = {};
private static final Set<AnnotationFilter> ANNOTATION_FILTERS;
static {
@@ -224,13 +238,15 @@ class ImportsContextCustomizer implements ContextCustomizer {
ANNOTATION_FILTERS = Collections.unmodifiableSet(filters);
}
private final Set<Annotation> annotations;
private final Set<Object> key;
ContextCustomizerKey(Class<?> testClass) {
Set<Annotation> annotations = new HashSet<Annotation>();
Set<Class<?>> seen = new HashSet<Class<?>>();
collectClassAnnotations(testClass, annotations, seen);
this.annotations = Collections.unmodifiableSet(annotations);
Set<Object> determinedImports = determineImports(annotations, testClass);
this.key = Collections.<Object>unmodifiableSet(
determinedImports != null ? determinedImports : annotations);
}
private void collectClassAnnotations(Class<?> classType,
@@ -266,17 +282,78 @@ class ImportsContextCustomizer implements ContextCustomizer {
return false;
}
private Set<Object> determineImports(Set<Annotation> annotations,
Class<?> testClass) {
Set<Object> determinedImports = new LinkedHashSet<Object>();
AnnotationMetadata testClassMetadata = new StandardAnnotationMetadata(
testClass);
for (Annotation annotation : annotations) {
for (Class<?> source : getImports(annotation)) {
Set<Object> determinedSourceImports = determineImports(source,
testClassMetadata);
if (determinedSourceImports == null) {
return null;
}
determinedImports.addAll(determinedSourceImports);
}
}
return determinedImports;
}
private Class<?>[] getImports(Annotation annotation) {
if (annotation instanceof Import) {
return ((Import) annotation).value();
}
return NO_IMPORTS;
}
private Set<Object> determineImports(Class<?> source,
AnnotationMetadata metadata) {
if (DeterminableImports.class.isAssignableFrom(source)) {
// We can determine the imports
return ((DeterminableImports) instantiate(source))
.determineImports(metadata);
}
if (ImportSelector.class.isAssignableFrom(source)
|| ImportBeanDefinitionRegistrar.class.isAssignableFrom(source)) {
// Standard ImportSelector and ImportBeanDefinitionRegistrar could
// use anything to determine the imports so we can't be sure
return null;
}
// The source itself is the import
return Collections.<Object>singleton(source.getName());
}
@SuppressWarnings("unchecked")
private <T> T instantiate(Class<T> source) {
try {
Constructor<?> constructor = source.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return (T) constructor.newInstance();
}
catch (Throwable ex) {
throw new IllegalStateException(
"Unable to instantiate DeterminableImportSelector "
+ source.getName(),
ex);
}
}
@Override
public int hashCode() {
return this.annotations.hashCode();
return this.key.hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj != null && getClass().equals(obj.getClass())
&& this.annotations.equals(((ContextCustomizerKey) obj).annotations));
&& this.key.equals(((ContextCustomizerKey) obj).key));
}
@Override
public String toString() {
return this.key.toString();
}
}
/**

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.
@@ -16,10 +16,21 @@
package org.springframework.boot.test.context;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.Set;
import kotlin.Metadata;
import org.junit.Test;
import org.spockframework.runtime.model.SpecMetadata;
import org.springframework.boot.context.annotation.DeterminableImports;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -29,6 +40,26 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class ImportsContextCustomizerTests {
@Test
public void importSelectorsCouldUseAnyAnnotations() throws Exception {
assertThat(new ImportsContextCustomizer(FirstImportSelectorAnnotatedClass.class))
.isNotEqualTo(new ImportsContextCustomizer(
SecondImportSelectorAnnotatedClass.class));
}
@Test
public void determinableImportSelector() throws Exception {
assertThat(new ImportsContextCustomizer(
FirstDeterminableImportSelectorAnnotatedClass.class))
.isEqualTo(new ImportsContextCustomizer(
SecondDeterminableImportSelectorAnnotatedClass.class));
}
@Test
public void importAutoConfigurationCanIgnoreAdditionalAnnotations() throws Exception {
}
@Test
public void customizersForTestClassesWithDifferentKotlinMetadataAreEqual() {
assertThat(new ImportsContextCustomizer(FirstKotlinAnnotatedTestClass.class))
@@ -43,6 +74,30 @@ public class ImportsContextCustomizerTests {
SecondSpockAnnotatedTestClass.class));
}
@Import(TestImportSelector.class)
@Indicator1
static class FirstImportSelectorAnnotatedClass {
}
@Import(TestImportSelector.class)
@Indicator2
static class SecondImportSelectorAnnotatedClass {
}
@Import(TestDeterminableImportSelector.class)
@Indicator1
static class FirstDeterminableImportSelectorAnnotatedClass {
}
@Import(TestDeterminableImportSelector.class)
@Indicator2
static class SecondDeterminableImportSelectorAnnotatedClass {
}
@Metadata(d2 = "foo")
static class FirstKotlinAnnotatedTestClass {
@@ -63,4 +118,43 @@ public class ImportsContextCustomizerTests {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Indicator1 {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Indicator2 {
}
static class TestImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata arg0) {
return new String[] {};
}
}
static class TestDeterminableImportSelector
implements ImportSelector, DeterminableImports {
@Override
public String[] selectImports(AnnotationMetadata arg0) {
return new String[] { TestConfig.class.getName() };
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.<Object>singleton(TestConfig.class.getName());
}
}
@Configuration
static class TestConfig {
}
}