Skip to content

Commit 062805f

Browse files
committed
Recursively process DeferredImportSelector properly
Previously, if a DeferredImportSelector was identified at a later stage as part of processing the collected set of deferred imports, such selector was processed as a regular import selector. Semantically, it makes sense as we already are in the deferred phase at this point and it doesn't make much sense to bother about the extra contract. However, Spring Framework 5 introduced the notion of Group that a deferred import selector can define. When it does, the container has to honour the contract of the Group rather than calling the simpler ImportSelector parent contract. This commit restructures the processing of such case. When a deferred import selector is detected while processing deferred import selectors, a group is created with only that selector and the group API is invoked. Issue: SPR-17351
1 parent 053820c commit 062805f

File tree

2 files changed

+258
-59
lines changed

2 files changed

+258
-59
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 102 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import java.util.Iterator;
3131
import java.util.LinkedHashMap;
3232
import java.util.LinkedHashSet;
33-
import java.util.LinkedList;
3433
import java.util.List;
3534
import java.util.Map;
3635
import java.util.Set;
@@ -138,8 +137,7 @@ class ConfigurationClassParser {
138137

139138
private final ImportStack importStack = new ImportStack();
140139

141-
@Nullable
142-
private List<DeferredImportSelectorHolder> deferredImportSelectors;
140+
private final DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();
143141

144142

145143
/**
@@ -162,8 +160,6 @@ public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
162160

163161

164162
public void parse(Set<BeanDefinitionHolder> configCandidates) {
165-
this.deferredImportSelectors = new LinkedList<>();
166-
167163
for (BeanDefinitionHolder holder : configCandidates) {
168164
BeanDefinition bd = holder.getBeanDefinition();
169165
try {
@@ -186,7 +182,7 @@ else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).h
186182
}
187183
}
188184

189-
processDeferredImportSelectors();
185+
this.deferredImportSelectorHandler.process();
190186
}
191187

192188
protected final void parse(@Nullable String className, String beanName) throws IOException {
@@ -543,53 +539,7 @@ private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, S
543539
}
544540
}
545541

546-
private void processDeferredImportSelectors() {
547-
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
548-
this.deferredImportSelectors = null;
549-
if (deferredImports == null) {
550-
return;
551-
}
552-
553-
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
554-
Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
555-
Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
556-
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
557-
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
558-
DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
559-
(group != null ? group : deferredImport),
560-
key -> new DeferredImportSelectorGrouping(createGroup(group)));
561-
grouping.add(deferredImport);
562-
configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
563-
deferredImport.getConfigurationClass());
564-
}
565-
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
566-
grouping.getImports().forEach(entry -> {
567-
ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata());
568-
try {
569-
processImports(configurationClass, asSourceClass(configurationClass),
570-
asSourceClasses(entry.getImportClassName()), false);
571-
}
572-
catch (BeanDefinitionStoreException ex) {
573-
throw ex;
574-
}
575-
catch (Throwable ex) {
576-
throw new BeanDefinitionStoreException(
577-
"Failed to process import candidates for configuration class [" +
578-
configurationClass.getMetadata().getClassName() + "]", ex);
579-
}
580-
});
581-
}
582-
}
583542

584-
private Group createGroup(@Nullable Class<? extends Group> type) {
585-
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
586-
Group group = BeanUtils.instantiateClass(effectiveType);
587-
ParserStrategyUtils.invokeAwareMethods(group,
588-
ConfigurationClassParser.this.environment,
589-
ConfigurationClassParser.this.resourceLoader,
590-
ConfigurationClassParser.this.registry);
591-
return group;
592-
}
593543

594544
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
595545
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
@@ -611,9 +561,9 @@ private void processImports(ConfigurationClass configClass, SourceClass currentS
611561
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
612562
ParserStrategyUtils.invokeAwareMethods(
613563
selector, this.environment, this.resourceLoader, this.registry);
614-
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
615-
this.deferredImportSelectors.add(
616-
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
564+
if (selector instanceof DeferredImportSelector) {
565+
this.deferredImportSelectorHandler.handle(
566+
configClass, (DeferredImportSelector) selector);
617567
}
618568
else {
619569
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
@@ -787,6 +737,103 @@ public String toString() {
787737
}
788738

789739

740+
private class DeferredImportSelectorHandler {
741+
742+
@Nullable
743+
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
744+
745+
/**
746+
* Handle the specified {@link DeferredImportSelector}. If deferred import
747+
* selectors are being collected, this registers this instance to the list. If
748+
* they are being processed, the {@link DeferredImportSelector} is also processed
749+
* immediately according to its {@link DeferredImportSelector.Group}.
750+
* @param configClass the source configuration class
751+
* @param importSelector the selector to handle
752+
*/
753+
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
754+
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
755+
configClass, importSelector);
756+
if (this.deferredImportSelectors == null) {
757+
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
758+
handler.register(holder);
759+
handler.processGroupImports();
760+
}
761+
else {
762+
this.deferredImportSelectors.add(holder);
763+
}
764+
}
765+
766+
public void process() {
767+
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
768+
this.deferredImportSelectors = null;
769+
try {
770+
if (deferredImports != null) {
771+
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
772+
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
773+
deferredImports.forEach(handler::register);
774+
handler.processGroupImports();
775+
}
776+
}
777+
finally {
778+
this.deferredImportSelectors = new ArrayList<>();
779+
}
780+
}
781+
782+
}
783+
784+
785+
private class DeferredImportSelectorGroupingHandler {
786+
787+
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
788+
789+
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
790+
791+
public void register(DeferredImportSelectorHolder deferredImport) {
792+
Class<? extends Group> group = deferredImport.getImportSelector()
793+
.getImportGroup();
794+
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
795+
(group != null ? group : deferredImport),
796+
key -> new DeferredImportSelectorGrouping(createGroup(group)));
797+
grouping.add(deferredImport);
798+
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
799+
deferredImport.getConfigurationClass());
800+
}
801+
802+
public void processGroupImports() {
803+
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
804+
grouping.getImports().forEach(entry -> {
805+
ConfigurationClass configurationClass = this.configurationClasses.get(
806+
entry.getMetadata());
807+
try {
808+
processImports(configurationClass, asSourceClass(configurationClass),
809+
asSourceClasses(entry.getImportClassName()), false);
810+
}
811+
catch (BeanDefinitionStoreException ex) {
812+
throw ex;
813+
}
814+
catch (Throwable ex) {
815+
throw new BeanDefinitionStoreException(
816+
"Failed to process import candidates for configuration class [" +
817+
configurationClass.getMetadata().getClassName() + "]", ex);
818+
}
819+
});
820+
}
821+
}
822+
823+
private Group createGroup(@Nullable Class<? extends Group> type) {
824+
Class<? extends Group> effectiveType = (type != null ? type
825+
: DefaultDeferredImportSelectorGroup.class);
826+
Group group = BeanUtils.instantiateClass(effectiveType);
827+
ParserStrategyUtils.invokeAwareMethods(group,
828+
ConfigurationClassParser.this.environment,
829+
ConfigurationClassParser.this.resourceLoader,
830+
ConfigurationClassParser.this.registry);
831+
return group;
832+
}
833+
834+
}
835+
836+
790837
private static class DeferredImportSelectorHolder {
791838

792839
private final ConfigurationClass configurationClass;

0 commit comments

Comments
 (0)