mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Reflect context hierarchy in beans endpoint’s response structure
Closes gh-10156
This commit is contained in:
parent
ab54801143
commit
a6b30a3aab
@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -32,7 +30,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Endpoint} to expose details of an application's bean, grouped by application
|
||||
* {@link Endpoint} to expose details of an application's beans, grouped by application
|
||||
* context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
@ -55,50 +53,53 @@ public class BeansEndpoint {
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Object> beans() {
|
||||
List<ApplicationContextDescriptor> contexts = new ArrayList<>();
|
||||
ConfigurableApplicationContext current = this.context;
|
||||
while (current != null) {
|
||||
contexts.add(ApplicationContextDescriptor.describing(current));
|
||||
current = getConfigurableParent(current);
|
||||
}
|
||||
return Collections.singletonMap("contexts", contexts);
|
||||
public ApplicationContextDescriptor beans() {
|
||||
return ApplicationContextDescriptor.describing(this.context);
|
||||
}
|
||||
|
||||
private ConfigurableApplicationContext getConfigurableParent(
|
||||
ConfigurableApplicationContext context) {
|
||||
ApplicationContext parent = context.getParent();
|
||||
if (parent instanceof ConfigurableApplicationContext) {
|
||||
return (ConfigurableApplicationContext) parent;
|
||||
/**
|
||||
* Response produced by the {@link BeansEndpoint}, primarily intended for
|
||||
* serialization to JSON.
|
||||
*/
|
||||
public static class BeansEndpointResponse {
|
||||
|
||||
private List<ApplicationContextDescriptor> contexts;
|
||||
|
||||
public BeansEndpointResponse(List<ApplicationContextDescriptor> contexts) {
|
||||
this.contexts = contexts;
|
||||
}
|
||||
return null;
|
||||
|
||||
public List<ApplicationContextDescriptor> getContexts() {
|
||||
return this.contexts;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A description of an application context, primarily intended for serialization to
|
||||
* JSON.
|
||||
*/
|
||||
static final class ApplicationContextDescriptor {
|
||||
public static final class ApplicationContextDescriptor {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String parentId;
|
||||
|
||||
private final Map<String, BeanDescriptor> beans;
|
||||
|
||||
private ApplicationContextDescriptor(String id, String parentId,
|
||||
Map<String, BeanDescriptor> beans) {
|
||||
private final ApplicationContextDescriptor parent;
|
||||
|
||||
private ApplicationContextDescriptor(String id, Map<String, BeanDescriptor> beans,
|
||||
ApplicationContextDescriptor parent) {
|
||||
this.id = id;
|
||||
this.parentId = parentId;
|
||||
this.beans = beans;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return this.parentId;
|
||||
public ApplicationContextDescriptor getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
public Map<String, BeanDescriptor> getBeans() {
|
||||
@ -107,10 +108,12 @@ public class BeansEndpoint {
|
||||
|
||||
private static ApplicationContextDescriptor describing(
|
||||
ConfigurableApplicationContext context) {
|
||||
ApplicationContext parent = context.getParent();
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
return new ApplicationContextDescriptor(context.getId(),
|
||||
parent == null ? null : parent.getId(),
|
||||
describeBeans(context.getBeanFactory()));
|
||||
describeBeans(context.getBeanFactory()),
|
||||
describing(getConfigurableParent(context)));
|
||||
}
|
||||
|
||||
private static Map<String, BeanDescriptor> describeBeans(
|
||||
@ -138,13 +141,22 @@ public class BeansEndpoint {
|
||||
&& (!bd.isLazyInit() || bf.containsSingleton(beanName)));
|
||||
}
|
||||
|
||||
private static ConfigurableApplicationContext getConfigurableParent(
|
||||
ConfigurableApplicationContext context) {
|
||||
ApplicationContext parent = context.getParent();
|
||||
if (parent instanceof ConfigurableApplicationContext) {
|
||||
return (ConfigurableApplicationContext) parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A description of a bean in an application context, primarily intended for
|
||||
* serialization to JSON.
|
||||
*/
|
||||
static final class BeanDescriptor {
|
||||
public static final class BeanDescriptor {
|
||||
|
||||
private final String[] aliases;
|
||||
|
||||
|
@ -43,27 +43,22 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class BeansEndpointTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void beansAreFound() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(EndpointConfiguration.class);
|
||||
contextRunner.run((context) -> {
|
||||
Map<String, Object> result = context.getBean(BeansEndpoint.class).beans();
|
||||
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result
|
||||
.get("contexts");
|
||||
assertThat(contexts).hasSize(1);
|
||||
ApplicationContextDescriptor contextDescriptor = contexts.get(0);
|
||||
assertThat(contextDescriptor.getParentId()).isNull();
|
||||
assertThat(contextDescriptor.getId()).isEqualTo(context.getId());
|
||||
Map<String, BeanDescriptor> beans = contextDescriptor.getBeans();
|
||||
ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class)
|
||||
.beans();
|
||||
assertThat(result.getParent()).isNull();
|
||||
assertThat(result.getId()).isEqualTo(context.getId());
|
||||
Map<String, BeanDescriptor> beans = result.getBeans();
|
||||
assertThat(beans.size())
|
||||
.isLessThanOrEqualTo(context.getBeanDefinitionCount());
|
||||
assertThat(contexts.get(0).getBeans()).containsKey("endpoint");
|
||||
assertThat(beans).containsKey("endpoint");
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void infrastructureBeansAreOmitted() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
@ -75,32 +70,28 @@ public class BeansEndpointTests {
|
||||
.filter((name) -> BeanDefinition.ROLE_INFRASTRUCTURE == factory
|
||||
.getBeanDefinition(name).getRole())
|
||||
.collect(Collectors.toList());
|
||||
Map<String, Object> result = context.getBean(BeansEndpoint.class).beans();
|
||||
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result
|
||||
.get("contexts");
|
||||
Map<String, BeanDescriptor> beans = contexts.get(0).getBeans();
|
||||
ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class)
|
||||
.beans();
|
||||
Map<String, BeanDescriptor> beans = result.getBeans();
|
||||
for (String infrastructureBean : infrastructureBeans) {
|
||||
assertThat(beans).doesNotContainKey(infrastructureBean);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void lazyBeansAreOmitted() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(EndpointConfiguration.class,
|
||||
LazyBeanConfiguration.class);
|
||||
contextRunner.run((context) -> {
|
||||
Map<String, Object> result = context.getBean(BeansEndpoint.class).beans();
|
||||
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result
|
||||
.get("contexts");
|
||||
ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class)
|
||||
.beans();
|
||||
assertThat(context).hasBean("lazyBean");
|
||||
assertThat(contexts.get(0).getBeans()).doesNotContainKey("lazyBean");
|
||||
assertThat(result.getBeans()).doesNotContainKey("lazyBean");
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void beansInParentContextAreFound() {
|
||||
ApplicationContextRunner parentRunner = new ApplicationContextRunner()
|
||||
@ -110,31 +101,9 @@ public class BeansEndpointTests {
|
||||
.withUserConfiguration(EndpointConfiguration.class).withParent(parent)
|
||||
.run(child -> {
|
||||
BeansEndpoint endpoint = child.getBean(BeansEndpoint.class);
|
||||
Map<String, Object> result = endpoint.beans();
|
||||
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result
|
||||
.get("contexts");
|
||||
assertThat(contexts).hasSize(2);
|
||||
assertThat(contexts.get(1).getBeans()).containsKey("bean");
|
||||
assertThat(contexts.get(0).getBeans()).containsKey("endpoint");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void beansInChildContextAreNotFound() {
|
||||
ApplicationContextRunner parentRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(EndpointConfiguration.class);
|
||||
parentRunner.run((parent) -> {
|
||||
new ApplicationContextRunner().withUserConfiguration(BeanConfiguration.class)
|
||||
.withParent(parent).run(child -> {
|
||||
BeansEndpoint endpoint = child.getBean(BeansEndpoint.class);
|
||||
Map<String, Object> result = endpoint.beans();
|
||||
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result
|
||||
.get("contexts");
|
||||
assertThat(contexts).hasSize(1);
|
||||
assertThat(contexts.get(0).getBeans()).containsKey("endpoint");
|
||||
assertThat(contexts.get(0).getBeans()).doesNotContainKey("bean");
|
||||
ApplicationContextDescriptor result = endpoint.beans();
|
||||
assertThat(result.getParent().getBeans()).containsKey("bean");
|
||||
assertThat(result.getBeans()).containsKey("endpoint");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -217,10 +217,8 @@ public class SampleActuatorApplicationTests {
|
||||
.withBasicAuth("user", getPassword())
|
||||
.getForEntity("/application/beans", Map.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(entity.getBody()).hasSize(1);
|
||||
Map<String, Object> body = (Map<String, Object>) ((List<?>) entity.getBody()
|
||||
.get("contexts")).get(0);
|
||||
assertThat(((String) body.get("id"))).startsWith("application");
|
||||
assertThat(entity.getBody()).containsOnlyKeys("beans", "parent", "id");
|
||||
assertThat(((String) entity.getBody().get("id"))).startsWith("application");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
Loading…
Reference in New Issue
Block a user