Refine ImportsContextCustomizer cache logic

Update `ImportsContextCustomizer` so that whenever possible a more
specific cache key is used.

Prior to this commit the customizer would generate a key based on *all*
annotations on the test class. This has repeatedly caused issues where
test classes that should have the same cache key did not due to
unrelated annotations.

A new `DeterminableImports` interface has been added that can be
implemented by `ImportSelector` and `ImportBeanDefinitionRegistrar`
implementations that are able to determine their imports early. The
existing `ImportAutoConfigurationImportSelector` and
`AutoConfigurationPackages` classes have been retrofitted with
this interface.

Fixes gh-7953
This commit is contained in:
Phillip Webb
2017-01-26 20:42:51 -08:00
parent aaf118c544
commit fa6a138598
8 changed files with 638 additions and 38 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.
@@ -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 {
}
}