Merge branch '1.5.x'

This commit is contained in:
Phillip Webb 2017-01-27 17:50:01 -08:00
commit 8747e039ee
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);
}
}
}

View File

@ -287,6 +287,88 @@ run the app.
[[cloud-deployment-aws]]
=== Amazon Web Services (AWS)
Amazon Web Services offers multiple ways to install Spring Boot based applications, either
as traditional web applications (war) or as executable jar files with an embedded web
server. Options include :
* AWS Elastic Beanstalk
* AWS Code Deploy
* AWS OPS Works
* AWS Cloud Formation
* AWS Container Registry
Each has different features and pricing model, here we will describe only the simplest
option : AWS Elastic Beanstalk.
==== AWS Elastic Beanstalk
As described in the official http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Java.html[Elastic
Beanstalk Java guide], there are two main options to deploy a Java application; You can
either us the "`Tomcat Platform`" or the "`Java SE platform`".
===== Using the Tomcat platform
This option applies to Spring Boot projects producing a war file. There is no any special
configuration required, just follow the official guide.
===== Using the Java SE platform
This option applies to Spring Boot projects producing a jar file and running an embedded
web container. Elastic Beanstalk environments run an nginx instance on port 80 to proxy
the actual application, running on port 5000. To configure it, add the following to your
`application.properties`:
[indent=0]
----
server.port=5000
----
===== Best practices
====== Uploading binaries instead of sources
By default Elastic Beanstalk uploads sources and compile them in AWS. To upload the
binaries instead, add the following to your `.elasticbeanstalk/config.yml` file:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
deploy:
artifact: target/demo-0.0.1-SNAPSHOT.jar
----
====== Reduce costs by setting the environment type
By default an Elastic Beanstalk environment is load balanced. The load balancer has a cost
perspective, to avoid it, set the environment type to "`Single instance`" as described
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-create-wizard.html#environments-create-wizard-capacity[in the Amazon documentation].
Single instance environments can be created using the CLI as well using the following
command:
[indent=0]
----
eb create -s
----
==== Summary
This is one of the easiest way to get to AWS, but there are more things
to cover, e.g.: how to integrate Elastic Beanstalk into any CI / CD tool, using the
Elastic Beanstalk maven plugin instead of the CLI, etc. There is a
https://exampledriven.wordpress.com/2017/01/09/spring-boot-aws-elastic-beanstalk-example/[blog]
covering these topics more in detail.
[[cloud-deployment-boxfuse]]
=== Boxfuse and Amazon Web Services
https://boxfuse.com/[Boxfuse] works by turning your Spring Boot executable jar or war

View File

@ -16,7 +16,6 @@
package org.springframework.boot.test.autoconfigure.data.mongo;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@ -24,8 +23,6 @@ import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* {@link TypeExcludeFilter} for {@link DataMongoTest @DataMongoTest}.
@ -63,15 +60,14 @@ class DataMongoTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter
return this.annotation.useDefaultFilters();
}
@Override
protected boolean defaultInclude(final MetadataReader metadataReader,
final MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@ -27,6 +27,7 @@ import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ObjectUtils;
/**
* Abstract base class for a {@link TypeExcludeFilter} that can be customized using an
@ -75,6 +76,11 @@ public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExclud
return true;
}
}
for (Class<?> component : getComponentIncludes()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, component)) {
return true;
}
}
return false;
}
@ -103,10 +109,50 @@ public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExclud
protected abstract Set<Class<?>> getDefaultIncludes();
protected abstract Set<Class<?>> getComponentIncludes();
protected enum FilterType {
INCLUDE, EXCLUDE
}
@Override
public int hashCode() {
final int prime = 31;
int result = 0;
result = prime * result + ObjectUtils.hashCode(hasAnnotation());
for (FilterType filterType : FilterType.values()) {
result = prime * result
+ ObjectUtils.nullSafeHashCode(getFilters(filterType));
}
result = prime * result + ObjectUtils.hashCode(isUseDefaultFilters());
result = prime * result + ObjectUtils.nullSafeHashCode(getDefaultIncludes());
result = prime * result + ObjectUtils.nullSafeHashCode(getComponentIncludes());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
AnnotationCustomizableTypeExcludeFilter other = (AnnotationCustomizableTypeExcludeFilter) obj;
boolean result = true;
result &= hasAnnotation() == other.hasAnnotation();
for (FilterType filterType : FilterType.values()) {
result &= ObjectUtils.nullSafeEquals(getFilters(filterType),
other.getFilters(filterType));
}
result &= isUseDefaultFilters() == other.isUseDefaultFilters();
result &= ObjectUtils.nullSafeEquals(getDefaultIncludes(),
other.getDefaultIncludes());
result &= ObjectUtils.nullSafeEquals(getComponentIncludes(),
other.getComponentIncludes());
return result;
};
}

View File

@ -18,6 +18,7 @@ package org.springframework.boot.test.autoconfigure.filter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@ -40,48 +41,65 @@ class TypeExcludeFiltersContextCustomizer implements ContextCustomizer {
private static final String EXCLUDE_FILTER_BEAN_NAME = TypeExcludeFilters.class
.getName();
private final Class<?> testClass;
private final Set<Class<? extends TypeExcludeFilter>> filterClasses;
private final Set<TypeExcludeFilter> filters;
TypeExcludeFiltersContextCustomizer(Class<?> testClass,
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
this.testClass = testClass;
this.filterClasses = filterClasses;
this.filters = instantiateTypeExcludeFilters(testClass, filterClasses);
}
private Set<TypeExcludeFilter> instantiateTypeExcludeFilters(Class<?> testClass,
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
Set<TypeExcludeFilter> filters = new LinkedHashSet<TypeExcludeFilter>();
for (Class<? extends TypeExcludeFilter> filterClass : filterClasses) {
filters.add(instantiateTypeExcludeFilter(testClass, filterClass));
}
return Collections.unmodifiableSet(filters);
}
private TypeExcludeFilter instantiateTypeExcludeFilter(Class<?> testClass,
Class<?> filterClass) {
try {
Constructor<?> constructor = getTypeExcludeFilterConstructor(filterClass);
ReflectionUtils.makeAccessible(constructor);
if (constructor.getParameterTypes().length == 1) {
return (TypeExcludeFilter) constructor.newInstance(testClass);
}
return (TypeExcludeFilter) constructor.newInstance();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create filter for " + filterClass,
ex);
}
}
@Override
public int hashCode() {
return this.filterClasses.hashCode();
return this.filters.hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj != null && getClass().equals(obj.getClass()) && this.filterClasses
.equals(((TypeExcludeFiltersContextCustomizer) obj).filterClasses));
return (obj != null && getClass().equals(obj.getClass()) && this.filters
.equals(((TypeExcludeFiltersContextCustomizer) obj).filters));
}
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedContextConfiguration) {
if (!this.filterClasses.isEmpty()) {
if (!this.filters.isEmpty()) {
context.getBeanFactory().registerSingleton(EXCLUDE_FILTER_BEAN_NAME,
createDelegatingTypeExcludeFilter());
}
}
private TypeExcludeFilter createDelegatingTypeExcludeFilter() {
final Set<TypeExcludeFilter> filters = new LinkedHashSet<TypeExcludeFilter>(
this.filterClasses.size());
for (Class<? extends TypeExcludeFilter> filterClass : this.filterClasses) {
filters.add(createTypeExcludeFilter(filterClass));
}
return new TypeExcludeFilter() {
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
for (TypeExcludeFilter filter : filters) {
for (TypeExcludeFilter filter : TypeExcludeFiltersContextCustomizer.this.filters) {
if (filter.match(metadataReader, metadataReaderFactory)) {
return true;
}
@ -92,20 +110,6 @@ class TypeExcludeFiltersContextCustomizer implements ContextCustomizer {
};
}
private TypeExcludeFilter createTypeExcludeFilter(Class<?> type) {
try {
Constructor<?> constructor = getTypeExcludeFilterConstructor(type);
ReflectionUtils.makeAccessible(constructor);
if (constructor.getParameterTypes().length == 1) {
return (TypeExcludeFilter) constructor.newInstance(this.testClass);
}
return (TypeExcludeFilter) constructor.newInstance();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create filter for " + type, ex);
}
}
private Constructor<?> getTypeExcludeFilterConstructor(Class<?> type)
throws NoSuchMethodException {
try {

View File

@ -16,7 +16,6 @@
package org.springframework.boot.test.autoconfigure.jdbc;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@ -24,8 +23,6 @@ import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* {@link TypeExcludeFilter} for {@link JdbcTest @JdbcTest}.
@ -62,15 +59,14 @@ class JdbcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return this.annotation.useDefaultFilters();
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@ -77,4 +77,9 @@ class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@ -16,7 +16,6 @@
package org.springframework.boot.test.autoconfigure.orm.jpa;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@ -24,8 +23,6 @@ import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* {@link TypeExcludeFilter} for {@link DataJpaTest @DataJpaTest}.
@ -62,15 +59,14 @@ class DataJpaTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return this.annotation.useDefaultFilters();
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@ -16,7 +16,7 @@
package org.springframework.boot.test.autoconfigure.web.client;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@ -26,8 +26,6 @@ import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
/**
@ -65,20 +63,6 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
RestClientTest.class);
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (super.defaultInclude(metadataReader, metadataReaderFactory)) {
return true;
}
for (Class<?> controller : this.annotation.components()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, controller)) {
return true;
}
}
return false;
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
@ -105,4 +89,9 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<Class<?>>(Arrays.asList(this.annotation.components()));
}
}

View File

@ -16,7 +16,7 @@
package org.springframework.boot.test.autoconfigure.web.servlet;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@ -29,8 +29,6 @@ import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBea
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ -74,20 +72,6 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
WebMvcTest.class);
}
@Override
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (super.defaultInclude(metadataReader, metadataReaderFactory)) {
return true;
}
for (Class<?> controller : this.annotation.controllers()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, controller)) {
return true;
}
}
return false;
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
@ -117,4 +101,9 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<Class<?>>(Arrays.asList(this.annotation.controllers()));
}
}

View File

@ -0,0 +1,158 @@
/*
* 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.
* 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.test.autoconfigure.cache;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.ExampleEntity;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@code ImportsContextCustomizerFactory} when used with
* {@link ImportAutoConfiguration}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ImportsContextCustomizerFactoryWithAutoConfigurationTests {
static ApplicationContext contextFromTest;
@Test
public void testClassesThatHaveSameAnnotationsShareAContext()
throws InitializationError {
RunNotifier notifier = new RunNotifier();
new SpringJUnit4ClassRunner(DataJpaTest1.class).run(notifier);
ApplicationContext test1Context = contextFromTest;
new SpringJUnit4ClassRunner(DataJpaTest3.class).run(notifier);
ApplicationContext test2Context = contextFromTest;
assertThat(test1Context).isSameAs(test2Context);
}
@Test
public void testClassesThatOnlyHaveDifferingUnrelatedAnnotationsShareAContext()
throws InitializationError {
RunNotifier notifier = new RunNotifier();
new SpringJUnit4ClassRunner(DataJpaTest1.class).run(notifier);
ApplicationContext test1Context = contextFromTest;
new SpringJUnit4ClassRunner(DataJpaTest2.class).run(notifier);
ApplicationContext test2Context = contextFromTest;
assertThat(test1Context).isSameAs(test2Context);
}
@Test
public void testClassesThatOnlyHaveDifferingPropertyMappedAnnotationAttributesDoNotShareAContext()
throws InitializationError {
RunNotifier notifier = new RunNotifier();
new SpringJUnit4ClassRunner(DataJpaTest1.class).run(notifier);
ApplicationContext test1Context = contextFromTest;
new SpringJUnit4ClassRunner(DataJpaTest4.class).run(notifier);
ApplicationContext test2Context = contextFromTest;
assertThat(test1Context).isNotSameAs(test2Context);
}
@DataJpaTest
@ContextConfiguration(classes = EmptyConfig.class)
@Unrelated1
public static class DataJpaTest1 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@ContextConfiguration(classes = EmptyConfig.class)
@DataJpaTest
@Unrelated2
public static class DataJpaTest2 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@ContextConfiguration(classes = EmptyConfig.class)
@DataJpaTest
@Unrelated1
public static class DataJpaTest3 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@ContextConfiguration(classes = EmptyConfig.class)
@DataJpaTest(showSql = false)
@Unrelated1
public static class DataJpaTest4 {
@Autowired
private ApplicationContext context;
@Test
public void test() {
contextFromTest = this.context;
}
}
@Retention(RetentionPolicy.RUNTIME)
static @interface Unrelated1 {
}
@Retention(RetentionPolicy.RUNTIME)
static @interface Unrelated2 {
}
@Configuration
@EntityScan(basePackageClasses = ExampleEntity.class)
static class EmptyConfig {
}
}

View File

@ -122,12 +122,22 @@ public class TypeExcludeFiltersContextCustomizerFactoryTests {
.equals(getClass().getName());
}
@Override
public int hashCode() {
return SimpleExclude.class.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj.getClass().equals(getClass());
}
}
static class TestClassAwareExclude extends SimpleExclude {
TestClassAwareExclude(Class<?> testClass) {
assertThat(testClass).isEqualTo(WithExcludeFilters.class);
assertThat(testClass).isNotNull();
}
}

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 {
}
}

View File

@ -36,7 +36,9 @@ import org.springframework.core.type.filter.TypeFilter;
* </pre>
* <p>
* Implementations should provide a subclass registered with {@link BeanFactory} and
* override the {@link #match(MetadataReader, MetadataReaderFactory)} method.
* override the {@link #match(MetadataReader, MetadataReaderFactory)} method. They should
* also implement a valid {@link #hashCode() hashCode} and {@link #equals(Object) equals}
* methods so that they can be used as part of Spring test's application context caches.
* <p>
* Note that {@code TypeExcludeFilters} are initialized very early in the application
* lifecycle, they should generally not have dependencies on any other beans. They are
@ -70,4 +72,16 @@ public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
return false;
}
@Override
public boolean equals(Object obj) {
throw new IllegalStateException(
"TypeExcludeFilter " + getClass() + " has not implemented equals");
}
@Override
public int hashCode() {
throw new IllegalStateException(
"TypeExcludeFilter " + getClass() + " has not implemented hashCode");
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.
* 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.context.annotation;
import java.util.Set;
import org.springframework.beans.factory.Aware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* Interface that can be implemented by {@link ImportSelector} and
* {@link ImportBeanDefinitionRegistrar} implementations when they can determine imports
* early. The {@link ImportSelector} and {@link ImportBeanDefinitionRegistrar} interfaces
* are quite flexible which can make it hard to tell exactly what bean definitions they
* will add. This interface should be used when an implementation consistently result in
* the same imports, given the same source.
* <p>
* Using {@link DeterminableImports} is particularly useful when working with Spring's
* testing support. It allows for better generation of {@link ApplicationContext} cache
* keys.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.5.0
*/
public interface DeterminableImports {
/**
* Return a set of objects that represent the imports. Objects within the returned
* {@code Set} must implement a valid {@link Object#hashCode() hashCode} and
* {@link Object#equals(Object) equals}.
* <p>
* Imports from multiple {@link DeterminableImports} instances may be combined by the
* caller to create a complete set.
* <p>
* Unlike {@link ImportSelector} and {@link ImportBeanDefinitionRegistrar} any
* {@link Aware} callbacks will not be invoked before this method is called.
* @param metadata the source meta-data
* @return a key representing the annotations that actually drive the import
*/
Set<Object> determineImports(AnnotationMetadata metadata);
}

View File

@ -0,0 +1,21 @@
/*
* 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.
* 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.
*/
/**
* Classes related to Spring's {@link org.springframework.context.ApplicationContext}
* annotations.
*/
package org.springframework.boot.context.annotation;