用SpringBoot开发久了,难免会遇到这样的疑问:这个类到底有没有被注册成Bean?自动装配到底给我塞进来了多少Bean?两个不同名字的配置类会不会注册出重复的Bean?这些问题看似简单,但如果对Spring容器的加载机制不熟悉,排查起来往往一头雾水。本文把查看容器内Bean的常用方法系统梳理一遍,并附上一些容易踩坑的细节。

一、为什么需要查看容器中的Bean
首先要明确一点:SpringBoot的应用由Spring容器统一管理对象,凡是被容器管理的对象都称为Bean。我们自己写的@Component、@Service、@Configuration标注的类会注册为Bean,各种starter通过自动装配机制注册的类也会成为Bean。一个普通的Web应用启动后,容器中通常有两百到六百个Bean,其中绝大部分来自自动装配。
排查以下几类问题时,查看Bean清单几乎是必经之路:第一,依赖冲突,比如不同版本的jar包注册了同名Bean导致启动报错;第二,条件装配失效,明明加了@ConditionalOnProperty却没有生效,需要确认Bean是否真的注册了;第三,性能分析,启动阶段某些Bean初始化过慢,需要定位是哪个Bean拖慢了启动速度;第四,学习自动装配原理,看看引入一个starter后容器里到底多了什么。
理解了使用场景,再看具体的查看方式就有的放矢了。下面介绍的方法各有适用场景,可以按需选择。
二、使用Actuator端点查看全部Bean
这是最推荐、最直观的方式。Actuator是SpringBoot官方提供的运维监控模块,其中的/actuator/beans端点可以直接返回容器中全部Bean的JSON清单,包含Bean名称、类型、作用域、是否单例、依赖关系等详细信息。
先引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>然后在配置文件中放开beans端点。出于安全考虑,默认只有health等少数端点暴露,需要显式配置:
management:
endpoints:
web:
exposure:
include: beans,info,health配置完成后启动应用,浏览器访问http://127.0.0.1:8080/actuator/beans,就能看到完整的Bean列表。返回的JSON按上下文分组,每个Bean条目包含bean、aliases、scope、type、dependencies等字段。信息量非常足,尤其适合分析依赖关系。
需要注意的是,生产环境不建议暴露beans端点,它会泄露应用的内部结构信息。建议只在开发和测试环境开启,或配合Spring Security做访问控制。
三、编程式获取:借助ApplicationContext
如果只是临时想打印一下Bean,不需要引入额外依赖,直接注入ApplicationContext就能搞定。ApplicationContext本身提供了多个查询方法,最常用的是getBeanDefinitionNames(),它返回容器中所有Bean定义的名称数组。
常见写法是定义一个CommandLineRunner,在应用启动完成后执行打印逻辑:
@Component
public class BeanListRunner implements CommandLineRunner {
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(String... args) {
String[] names = applicationContext.getBeanDefinitionNames();
Arrays.stream(names)
.sorted()
.forEach(name -> System.out.println(name + " : "
+ applicationContext.getType(name)));
}
}启动应用后控制台会输出排序后的Bean名称和对应类型,一目了然。如果想进一步筛选,比如只看自己写的Bean,可以结合BeanFactory的具体实现类型判断。另外还有几个实用的查询方法:getBeansOfType(xxx.class)获取某类型的全部Bean,containsBean("name")判断某个名称的Bean是否存在,getBeansWithAnnotation(xxx.class)获取带某注解的全部Bean。
举个实际例子,想知道所有标注了@Service的Bean:
Map<String, Object> services =
applicationContext.getBeansWithAnnotation(Service.class);
services.forEach((name, bean) -> System.out.println(name));这种方式灵活度最高,可以根据业务需要任意加工输出结果,缺点是要写代码,适合开发阶段的临时排查。
四、借助开发工具和启动日志辅助分析
除了上面两种主动查询方式,还有一些辅助手段值得了解。第一种是IDEA的支持。IntelliJ IDEA对Spring有深度集成,打开项目后按Ctrl+Alt+Shift+N搜索Bean名称,或者通过Spring工具窗口查看Bean之间的依赖关系图。在@Autowired注入点上按快捷键跳转,也能直接定位到Bean的来源,这对分析自动装配来源特别有用。
第二种是条件装配报告。在配置文件中加上debug=true(或者启动参数加--debug),SpringBoot会在启动日志中输出一份CONDITIONS EVALUATION REPORT,详细列出每个自动配置类的匹配结果,哪些匹配成功、哪些因为什么条件不满足被跳过,全部写得清清楚楚。当怀疑某个自动配置没生效时,这份报告比任何猜测都可靠。
第三种是第三方的启动分析工具,比如 spring-startup-analyzer 这类项目,能统计每个Bean的初始化耗时,对优化启动速度很有帮助。此外IDEA 2021之后的版本自带启动分析面板(Startup Profile),可以直接看到各阶段Bean初始化的时间分布。
五、常见问题与注意事项
查看Bean时经常会遇到一些让人困惑的现象,这里集中说明几个高频问题。
第一,为什么打印出来的Bean数量比想象中少?最常见的原因是懒加载。如果Bean定义了@Lazy,只有在首次使用时才会实例化,但getBeanDefinitionNames()返回的是Bean定义而非实例,理论上仍会列出。而Actuator的beans端点在默认非懒加载的单例模式下展示的是已创建的Bean,如果全局开启了懒加载(spring.main.lazy-initialization=true),端点里看到的Bean会明显减少,需要注意区分。
第二,同名Bean冲突。当两个配置类用相同方法名注册Bean,或者@ComponentScan的包路径重叠时,会抛出BeanDefinitionOverrideException。可以通过spring.main.allow-bean-definition-overiding=true允许覆盖,但更推荐的做法是排查来源、从根本上消除冲突, Actuator或启动日志都会明确指出冲突的Bean名称。
第三,条件注解导致的Bean缺失。加了@ConditionalOnClass、@ConditionalOnProperty等注解的配置类,条件不满足时整个配置会被静默跳过,没有任何报错。如果发现预期的Bean不在列表里,优先用debug=true查看条件评估报告,而不是盲目加注解。
第四,父子上下文问题。传统SSM项目整合时可能存在Root Context和Servlet Context两个容器,某些Bean只在一个容器里可见。SpringBoot默认是单一容器,问题不大,但如果嵌入了老的XML配置或引入了特定的MVC分层配置,就可能踩到这个坑,表现为一个地方能查到Bean,另一个地方查不到。
总结一下:临时排查用ApplicationContext编程打印,系统查看用Actuator的beans端点,分析自动装配用条件报告,配合IDEA的可视化工具基本可以覆盖所有场景。掌握这些手段之后,容器里的Bean对你来说就不再是黑盒了。
SpringBoot查看BeanSpring Bean管理Actuator修改时间:2026-09-04 07:12:38