Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,20 @@
* class will be subject to the conditions.
*
* <p><strong>NOTE</strong>: Inheritance of {@code @Conditional} annotations
* is not supported; any conditions from superclasses or from overridden
* methods will not be considered. In order to enforce these semantics,
* {@code @Conditional} itself is not declared as
* {@link java.lang.annotation.Inherited @Inherited}; furthermore, any
* custom <em>composed annotation</em> that is meta-annotated with
* {@code @Conditional} must not be declared as {@code @Inherited}.
* from superclasses or from overridden methods is not supported. To enforce
* these semantics, {@code @Conditional} is not declared as
* {@link java.lang.annotation.Inherited @Inherited}, and any custom
* <em>composed annotation</em> meta-annotated with {@code @Conditional}
* must not be declared as {@code @Inherited} either.
*
* <p>Conditions declared on a lexically enclosing class, however, do gate
* registration of any nested static {@code @Configuration} classes within it.
* This applies regardless of how the nested class is discovered: as a member
* of its enclosing class, via {@link Import @Import}, via
* {@link ComponentScan @ComponentScan}, or through direct registration.
* The single exception is a nested class imported via {@code @Import} from
* outside its enclosing class, in which case only the importer's conditions
* apply.
*
* @author Phillip Webb
* @author Sam Brannen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.context.annotation;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
Expand Down Expand Up @@ -54,6 +55,7 @@
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
Expand Down Expand Up @@ -95,14 +97,16 @@ class ConfigurationClassBeanDefinitionReader {

private final ConditionEvaluator conditionEvaluator;

private final MetadataReaderFactory metadataReaderFactory;


/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance
* that will be used to populate the given {@link BeanDefinitionRegistry}.
*/
ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator,
ImportRegistry importRegistry) {
ImportRegistry importRegistry, MetadataReaderFactory metadataReaderFactory) {

this.registry = registry;
this.sourceExtractor = sourceExtractor;
Expand All @@ -111,6 +115,7 @@ class ConfigurationClassBeanDefinitionReader {
this.importBeanNameGenerator = importBeanNameGenerator;
this.importRegistry = importRegistry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
this.metadataReaderFactory = metadataReaderFactory;
}


Expand Down Expand Up @@ -524,11 +529,34 @@ public boolean shouldSkip(ConfigurationClass configClass) {
}
if (skip == null) {
skip = conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN);
if (!skip && !configClass.isImported()) {
// Non-imported nested @Configuration: also honor REGISTER_BEAN-phase
// conditions declared on any lexically enclosing class.
skip = shouldSkipFromEnclosingClasses(configClass.getMetadata());
}
}
this.skipped.put(configClass, skip);
}
return skip;
}

private boolean shouldSkipFromEnclosingClasses(AnnotationMetadata metadata) {
String enclosingClassName = metadata.getEnclosingClassName();
while (enclosingClassName != null) {
AnnotationMetadata enclosing;
try {
enclosing = metadataReaderFactory.getMetadataReader(enclosingClassName).getAnnotationMetadata();
}
catch (IOException ex) {
return false;
}
if (conditionEvaluator.shouldSkip(enclosing, ConfigurationPhase.REGISTER_BEAN)) {
return true;
}
enclosingClassName = enclosing.getEnclosingClassName();
}
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ protected void processConfigurationClass(ConfigurationClass configClass, Predica
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
if (!configClass.isImported() && shouldSkipFromEnclosingClasses(
configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}

ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
Expand Down Expand Up @@ -735,6 +739,13 @@ private List<Condition> collectRegisterBeanConditions(ConfigurationClass configu
if (enclosingConfigurationClass != null) {
allConditions.addAll(this.conditionEvaluator.collectConditions(enclosingConfigurationClass.getMetadata()));
}
if (!configurationClass.isImported()) {
for (AnnotationMetadata enclosing = getEnclosingClassMetadata(metadata);
enclosing != null;
enclosing = getEnclosingClassMetadata(enclosing)) {
allConditions.addAll(this.conditionEvaluator.collectConditions(enclosing));
}
}
return allConditions.stream().filter(REGISTER_BEAN_CONDITION_FILTER).toList();
}

Expand All @@ -748,6 +759,39 @@ private List<Condition> collectRegisterBeanConditions(ConfigurationClass configu
return null;
}

/**
* Determine whether any class in the lexical enclosing chain of the given
* metadata declares a {@code @Conditional} that vetoes registration for the
* given phase. Used to extend enclosing-class condition inheritance to
* nested {@code @Configuration} classes discovered via {@code @ComponentScan}
* or direct registration (paths where the enclosing class is not present in
* {@code importedBy}).
*/
private boolean shouldSkipFromEnclosingClasses(AnnotationMetadata metadata, ConfigurationPhase phase) {
for (AnnotationMetadata enclosing = getEnclosingClassMetadata(metadata);
enclosing != null;
enclosing = getEnclosingClassMetadata(enclosing)) {
if (this.conditionEvaluator.shouldSkip(enclosing, phase)) {
return true;
}
}
return false;
}

private @Nullable AnnotationMetadata getEnclosingClassMetadata(AnnotationMetadata metadata) {
String enclosingClassName = metadata.getEnclosingClassName();
if (enclosingClassName == null) {
return null;
}
try {
return this.metadataReaderFactory.getMetadataReader(enclosingClassName).getAnnotationMetadata();
}
catch (IOException ex) {
// Enclosing metadata not readable — treat as if no condition applies.
return null;
}
}


@SuppressWarnings("serial")
private class ImportStack extends ArrayDeque<ConfigurationClass> implements ImportRegistry {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@ else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
this.importBeanNameGenerator, parser.getImportRegistry(),
this.metadataReaderFactory);
}
this.reader.loadBeanDefinitions(configClasses);
for (ConfigurationClass configClass : configClasses) {
Expand Down
Loading