From 44e0c3f4669e3bdea5cc6d20f545431f474c17ea Mon Sep 17 00:00:00 2001 From: Reynolds Liu Date: Mon, 25 Aug 2025 08:47:35 +0800 Subject: [PATCH] Fix: Include child contexts in auto-configuration conditions report Previously, ConditionsReportEndpoint only traversed the parent ApplicationContext hierarchy, which caused conditions from child contexts (e.g. the Actuator's management context) to be missing from the report. This commit updates the context collection logic to: - Traverse both parent and child ApplicationContexts - Use seenIds to avoid duplicate or cyclic references - Ensure child contexts (like Actuator context) are now included in the conditions evaluation report Closes gh-6698 Signed-off-by: Reynolds Liu --- .../condition/ConditionsReportEndpoint.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java b/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java index ba2439b8f7cc..4b74a8ef17b4 100644 --- a/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java +++ b/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -64,14 +65,37 @@ public ConditionsReportEndpoint(ConfigurableApplicationContext context) { @ReadOperation public ConditionsDescriptor conditions() { Map contextConditionEvaluations = new HashMap<>(); - ConfigurableApplicationContext target = this.context; - while (target != null) { - contextConditionEvaluations.put(target.getId(), new ContextConditionsDescriptor(target)); - target = getConfigurableParent(target); + List allContexts = collectAllContexts(this.context); + for (ConfigurableApplicationContext ctx : allContexts) { + String ctxId = ctx.getId(); + if (ctxId != null) { + contextConditionEvaluations.put(ctxId, new ContextConditionsDescriptor(ctx)); + } } return new ConditionsDescriptor(contextConditionEvaluations); } + List collectAllContexts(ConfigurableApplicationContext rootContext) { + List contexts = new ArrayList<>(); + Set seenIds = new HashSet<>(); + ConfigurableApplicationContext current = rootContext; + + while (current != null && current.getId() != null && seenIds.add(current.getId())) { + contexts.add(current); + current = getConfigurableParent(current); + } + + Map found = rootContext + .getBeansOfType(ConfigurableApplicationContext.class, false, false); + for (ConfigurableApplicationContext ctx : found.values()) { + String ctxId = ctx.getId(); + if (ctxId != null && seenIds.add(ctxId)) { + contexts.add(ctx); + } + } + return contexts; + } + private @Nullable ConfigurableApplicationContext getConfigurableParent(ConfigurableApplicationContext context) { ApplicationContext parent = context.getParent(); if (parent instanceof ConfigurableApplicationContext configurableParent) {