写点什么

Spring Boot「11」查看所有托管的 Bean

作者:Samson
  • 2022-10-22
    上海
  • 本文字数:3019 字

    阅读完需:约 10 分钟

Spring Boot「11」查看所有托管的 Bean

今天我们将一块学习一个小技巧,如何在 Spring Boot 应用中查看所有托管在容器中的 Bean?


在 Spring / Spring Boot 应用中,核心功能之一 IoC 容器,它负责所有托管 Bean 的生命周期管理。获得容器中所有托管 Bean 的方式主要有两种:


  • 使用 ListableBeanFactory 接口

  • 使用 Actuator

01-ListableBeanFactory 接口

Spring 中 ApplicationContext 接口继承了 ListableBeanFactory 接口。因此,所有的应用上下文都实现了 ListableBeanFactory 接口,具有该接口定义的所有方法实现,其中就包括我们要使用的:


String[] getBeanDefinitionNames();
复制代码


它能够返回容器中注册的所有 Bean 的名称。


有了这个接口,我们就可以在启动程序后在控制台打印所有 Bean 的名称:


@SpringBootApplicationpublic class ListAllManagedBeanApplication {    public static void main(String[] args) {        final ConfigurableApplicationContext ctx = SpringApplication.run(ListAllManagedBeanApplication.class, args);
final String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); Arrays.stream(beanDefinitionNames).forEach(System.out::println); }}
复制代码


我们能够得到如下输出:


listAllManagedBeanApplication// 省略其他的 bean
复制代码


@SpringBootApplication注解等价于@EnableAutoConfiguration+@Configuration+@ComponentScan。其中,@EnableAutoConfiguration会根据 classpath 下的包的不同进行自动话配置,所以上述输出的 Bean 名称会比较多,而且由于每个项目依赖的不同,输出的 Bean 名称也不尽相同;@Configuration@Component的子类型,在@ComponentScan作用下,会为其创建一个 Bean,并托管到容器中。


将所有的 Bean 名称打印在控制台的意义可能不大,在开发阶段或许能帮助开发者排查问题,在其他测试阶段,帮助并不太大。


我们可以稍微换个思路,在 Spring Boot 项目中,创建 HTTP API 是非常方便的,所以我们可创建一个 HTTP API,通过访问该接口来获得当前容器中所有的 Bean 名称。我们新建一个 ManagedBeanVisitorController 类,用来处理 HTTP 请求。


@RestController@RequestMapping("/managed")public class ManagedBeanVisitorController implements ApplicationContextAware {
private ApplicationContext applicationContext;
@GetMapping("/beans") CollectionModel<String> all() { final List<String> beanDefinitionNames = Arrays.stream(this.applicationContext.getBeanDefinitionNames()).collect(Collectors.toList()); return CollectionModel.of(beanDefinitionNames, linkTo(methodOn(ManagedBeanVisitorController.class).all()).withSelfRel()); }

@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
复制代码


因为需要用到 ApplicationContext 中的 getBeanDefinitionNames 方法,所以我们让 ManagedBeanVisitorController 实现 ApplicationContextAware 接口来获得容器的引用。


然后,我们运行程序,并访问 http://localhost:8080/magaged/beans 就会得到当前容器中所有 Bean 的名称列表:


{  "_embedded": {    "stringList": [      "listAllManagedBeanApplication",      "managedBeanVisitorController",      // 省略其他的 Bean    ]  },  "_links": {    "self": {      "href": "http://localhost:8080/managed/beans"    }  }}
复制代码


我们在定义接口返回值时,通过 spring-hateoas 来生成 RESTful 风格的 API 接口,更多应用细节可以参考之前的问题x;

02-Spring Actuator

Spring Boot 提供了开箱即用的应用监控、检测工具 Actuator,主要用来查看应用的各种信息,例如应用健康状态、各类指标、日志、转储、环境等其他信息。而且 Actuator 提供了基于 HTTP API 或 JMX Bean 的交互方式,开发者可以非常容易的获取这些信息。


Spring Actuator 优点类似于上面我们通过 HTTP API 提供的查看接口,只不过共能更完善,也更丰富。接下来,让我们来体验下如何使用 Spring Actuator 吧。


默认情况下,Spring Actuator 中的 Endpoint 是不开启的,仅开启了 /health。通过 HTTP 访问 Actuator 的方式与上节中的方式类似,通过浏览器访问 http://localhost:8080/actuator/,可以查看当前开启的 Endpoint,例如:


{  "_links": {    "self": {      "href": "http://localhost:8080/actuator",      "templated": false    },    "health": {      "href": "http://localhost:8080/actuator/health",      "templated": false    },    "health-path": {      "href": "http://localhost:8080/actuator/health/{*path}",      "templated": true    }  }}
复制代码


Actuator 中包含了一个 /beans Endpoint,与上节中我们实现的 Controller 功能类似。接下来,我们以此为例,看一下如何在项目中开启这个 Endpoint。


首先,可以通过management.endpoints.web.exposure.include=health,info,env,beans来激活 /health | /info | /env | /beans 这四个 Endpoint。之后访问 http://localhost:8080/actuator/ 就能看到开启的 Endpoint 发生了变化:


{  "_links": {    "self": {      "href": "http://localhost:8080/actuator",      "templated": false    },    "beans": {      "href": "http://localhost:8080/actuator/beans",      "templated": false    },    "health": {      "href": "http://localhost:8080/actuator/health",      "templated": false    },    "health-path": {      "href": "http://localhost:8080/actuator/health/{*path}",      "templated": true    },    "info": {      "href": "http://localhost:8080/actuator/info",      "templated": false    },    "env": {      "href": "http://localhost:8080/actuator/env",      "templated": false    },    "env-toMatch": {      "href": "http://localhost:8080/actuator/env/{toMatch}",      "templated": true    }  }}
复制代码


当我们访问 http://localhost:8080/actuator/beans 时,同样也会返回容器中所有的 Bean 信息,只不过比我们之前自己实现的更丰富:


"listAllManagedBeanApplication": {  "aliases": [],  "scope": "singleton",  "type": "self.samson.example.core.ListAllManagedBeanApplication$$EnhancerBySpringCGLIB$$e6d36e5c",  "resource": null,  "dependencies": []},"managedBeanVisitorController": {  "aliases": [],  "scope": "singleton",  "type": "self.samson.example.core.ManagedBeanVisitorController",  "resource": "file [ManagedBeanVisitorController.class]",  "dependencies": []},
复制代码


其他的 Endpoint 示例不再深入讨论,更多关于 Actuator 信息可以参考官方文档1

03-总结

今天我们通过两个示例展示了如何获得 Spring Boot IoC 容器中的所有 Bean。在开发阶段,如果仅出于易于调试的目的,可以通过 ApplicationContext 中的 getBeanDefinitionNames 来获得所有的 Bean 名称。但如果在生产环境,如果有监控应用的必要,还是推荐使用 Spring Actuator,获得的信息更丰富,与应用的耦合程度也更低。

发布于: 刚刚阅读数: 3
用户头像

Samson

关注

还未添加个人签名 2019-07-22 加入

InfoQ签约作者 | 阿里云社区签约作者

评论

发布
暂无评论
Spring Boot「11」查看所有托管的 Bean_Java_Samson_InfoQ写作社区