Springboot启动了哪些bean?这两种方式可以获取

本文详细介绍了如何通过ListableBeanFactory接口和SpringBootActuator在Spring应用中列出所有管理的bean,帮助开发者诊断bean未启动问题。涵盖了@Autowired注解、Bean定义、Actuator监控等内容。

1. 概述

在本文中,我们将探索在容器中获取所有spring管理的bean的相关技术。这有神马用?主要是用于排查问题。一般都是我们创建的某一个bean没有启动的问题。毕竟工作中总是会遇到各种各样的bug。提前了解一些没有坏处。

2. IoC容器

bean是spring管理的应用程序的基础,所有bean都驻留在IOC容器中,该容器负责管理它们的生命周期。

我们可以通过两种方式获取该容器内所有bean的列表:

  1. 使用ListableBeanFactory接口
  2. 使用Spring Boot Actuator

3.使用ListableBeanFactory接口

ListableBeanFactory接口提供了getBeanDefinitionNames()方法,该方法返回在这个工厂中定义的所有bean的名称。您可以在官方文档中找到所有已知子接口及其实现类的列表。我们来看这种方式如何获取所有的bean。

第一步:创建一个Controller

@Controller
public class FooController {
    @Autowired
    private FooService fooService;
    @RequestMapping(value="/displayallbeans")
    public String getHeaderAndBody(Map model){
        model.put("header", fooService.getHeader());
        model.put("message", fooService.getBody());
        return "displayallbeans";
    }
}

这个Controller依赖于另一个FooService。

第二步:创建Service

@Service
public class FooService {
    public String getHeader() {
        return "Display All Beans";
    }
    public String getBody() {
        return "展示所有beans的案例";
    }
}

注意,我们在这里创建了两个不同的bean:

  • fooController

  • fooService

这里使用applicationContext对象并调用它的getBeanDefinitionNames()方法,该方法将返回applicationContext容器中的所有bean:

第三步:设置SpringBootApplication启动类

@SpringBootApplication
public class DemoApplication {
    private static ApplicationContext applicationContext;
    public static void main(String[] args) {
        applicationContext = SpringApplication.run(DemoApplication.classargs);
        displayAllBeans();
    }
    public static void displayAllBeans() {
        String[] allBeanNames = applicationContext.getBeanDefinitionNames();
        for(String beanName : allBeanNames) {
            System.out.println(beanName);
        }
    }
}

第四步:测试打印

这将打印applicationContext容器中的所有bean:

注意,除了我们定义的bean之外,它还将记录该容器中的所有其他bean。为了清楚起见,我们在这里省略了它们,因为它们有很多。

4. 使用Spring Boot Actuator

Spring Boot Actuator提供了用于监控应用程序统计信息的端点。下面看看这种方式:

第一步:添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

第二步:修改application.properties

management.endpoints.web.exposure.include=*

把上面代码添加到properties文件中。

第三步:使用发布端点查看

由于这里的Actuator没有配置,所以显示的比较乱。关于Actuator的配置,会在下一篇文章中呈现。

5. 结论

在本文中,我们了解了如何使用ListableBeanFactory接口和Spring Boot Actuator在Spring IoC容器中显示所有bean。希望对你有点帮助。

<think>我们正在讨论Spring Boot在启动获取Bean的方法。根据之前的对话历史和引用内容,我们可以总结出以下几种方法: 1. 使用`@PostConstruct`注解:在Bean初始化方法上添加此注解,该方法会在依赖注入完成后执行。 2. 实现`BeanPostProcessor`接口:通过实现该接口,可以在每个Bean初始化前后执行自定义逻辑。 3. 监听`ContextRefreshedEvent`事件:当ApplicationContext初始化完成或刷新时,会触发该事件,此时可以安全地获取Bean。 4. 使用`ApplicationRunner`或`CommandLineRunner`接口:这两个接口会在Spring Boot应用启动完成后执行,此时所有的Bean都已经初始化完毕。 此外,引用[3]中提到了Spring Boot启动过程中会发布`ContextRefreshedEvent`事件,以及容器刷新后的处理步骤,其中就包括执行`SmartInitializingSingleton`接口的`afterSingletonsInstantiated`方法。 因此,我们可以补充另一种方法: 5. 实现`SmartInitializingSingleton`接口:该接口的方法`afterSingletonsInstantiated`会在所有单例Bean初始化完成后调用。 下面,我们将详细说明这些方法,并给出示例代码。 注意:在回答中,我们会引用用户提供的引用标记(如[^1])来支持我们的内容。 ### 方法1:使用`@PostConstruct`注解 在Bean初始化方法上添加`@PostConstruct`注解,该方法会在Bean的依赖注入完成后执行。此时,Bean已经初始化,因此可以在这个方法中操作其他Bean。 示例代码: ```java import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class MyBean { @PostConstruct public void init() { // 在这个方法中,可以安全地使用依赖注入的Bean System.out.println("MyBean initialized"); } } ``` 注意:`@PostConstruct`是JSR-250规范的一部分,需要引入`javax.annotation`依赖(在Spring Boot中通常已经包含)。 ### 方法2:实现`BeanPostProcessor`接口 通过实现`BeanPostProcessor`接口,可以在每个Bean初始化前后执行自定义代码。其中,`postProcessAfterInitialization`方法在Bean初始化完成后执行,此时可以获取到该Bean的实例。 示例代码: ```java import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 在Bean初始化后执行 // 注意:这里会处理所有的Bean,因此通常需要根据beanName或bean的类型进行过滤 if (bean instanceof MyBean) { System.out.println("MyBean initialized and processed by BeanPostProcessor"); } return bean; } } ``` 注意:`BeanPostProcessor`会处理容器中所有的Bean,因此要小心使用,避免性能问题。 ### 方法3:监听`ContextRefreshedEvent`事件 当ApplicationContext初始化或刷新完成时,会发布`ContextRefreshedEvent`事件。我们可以创建一个监听器来监听这个事件,并在事件发生时获取Bean。 示例代码: ```java import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @Component public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 通过event获取ApplicationContext,然后获取Bean MyBean myBean = event.getApplicationContext().getBean(MyBean.class); System.out.println("Context refreshed, got MyBean: " + myBean); } } ``` 注意:在Spring Boot中,`ContextRefreshedEvent`可能会被触发多次(例如,在父子容器的情况下),因此需要做好判断。 ### 方法4:使用`ApplicationRunner`或`CommandLineRunner`接口 这两个接口是在Spring Boot启动完成后执行的,此时所有的Bean都已经初始化完成。我们可以通过依赖注入的方式获取Bean,然后在`run`方法中执行初始化代码。 示例代码(使用`ApplicationRunner`): ```java import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class MyApplicationRunner implements ApplicationRunner { private final MyBean myBean; public MyApplicationRunner(MyBean myBean) { this.myBean = myBean; } @Override public void run(ApplicationArguments args) throws Exception { // 在Spring Boot启动完成后执行 System.out.println("Application started, got MyBean: " + myBean); } } ``` 同样,也可以使用`CommandLineRunner`,用法类似,只是`run`方法的参数是字符串数组。 ### 方法5:实现`SmartInitializingSingleton`接口 在Spring框架中,`SmartInitializingSingleton`接口提供了一个方法`afterSingletonsInstantiated`,该方法会在所有的单例Bean初始化完成后调用。我们可以让Bean实现这个接口,然后在该方法中执行初始化代码。 示例代码: ```java import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.stereotype.Component; @Component public class MySmartBean implements SmartInitializingSingleton { @Override public void afterSingletonsInstantiated() { // 当所有单例Bean初始化完成后,执行此方法 System.out.println("All singletons have been initialized"); } } ``` 注意:这个方法是在Spring容器刷新过程的最后阶段调用的,所以此时所有的单例Bean都已经初始化完成。 ### 比较与选择 - `@PostConstruct`:适用于单个Bean初始化,简单直接。 - `BeanPostProcessor`:适用于对所有Bean进行后处理,功能强大但需谨慎使用。 - `ContextRefreshedEvent`:适用于需要在容器完全刷新后执行一次的任务,但要注意可能会被多次触发。 - `ApplicationRunner`/`CommandLineRunner`:Spring Boot特有,适用于应用启动后执行任务,可以访问命令行参数。 - `SmartInitializingSingleton`:适用于在所有单例Bean初始化完成后执行的任务。 根据实际需求选择合适的方法。如果只是当前Bean初始化后需要执行一些操作,使用`@PostConstruct`;如果需要在所有Bean初始化完成后执行,可以选择`SmartInitializingSingleton`或者`ContextRefreshedEvent`;如果需要在应用启动后执行(比如需要访问命令行参数),则选择`ApplicationRunner`或`CommandLineRunner`。 以上方法在Spring Boot中都是可靠的,可以根据具体场景选择使用。 §§相关问题§§ 1. Spring Boot中如何避免在获取Bean时出现循环依赖问题? 2. 如何控制Spring Boot中Bean初始化顺序? 3. Spring Boot启动过程中有哪些关键事件,它们的触发顺序是怎样的? </think>根据引用内容和Spring Boot的设计原理,以下是容器初始化获取Bean的可靠方法,按执行时机排序: ### 一、初始化阶段获取Bean的方法 1. **`@PostConstruct` 方法注入** 在Bean依赖注入完成后立即执行: ```java import javax.annotation.PostConstruct; import org.springframework.stereotype.Component; @Component public class DatabaseInitializer { @PostConstruct public void init() { // 直接使用依赖注入的Bean userRepository.save(new User("admin")); // 安全操作 } } ``` > ✅ **最佳场景**:单个Bean初始化操作(如缓存预热)[^1] 2. **`SmartInitializingSingleton` 接口** 在所有**单例Bean实例化后**触发(早于Runner): ```java import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.stereotype.Component; @Component public class CachePreloader implements SmartInitializingSingleton { @Override public void afterSingletonsInstantiated() { // 可直接获取所有单例Bean MyBean bean = applicationContext.getBean(MyBean.class); bean.loadCache(); } } ``` > ✅ **最佳场景**:跨Bean初始化协作(如初始化顺序控制)[^3] --- ### 二、容器就绪后获取Bean的方法 3. **`ContextRefreshedEvent` 事件监听** 容器**完全刷新后**触发(所有Bean就绪): ```java import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.ApplicationListener; @Component public class StartupListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 通过事件获取ApplicationContext MyService service = event.getApplicationContext() .getBean(MyService.class); service.verifyConfig(); } } ``` > ✅ **最佳场景**:全局初始化检查或系统自检[^3] 4. **`ApplicationRunner` / `CommandLineRunner`** 在**应用启动完成后**执行(可访问命令行参数): ```java import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class DataMigrator implements ApplicationRunner { private final UserRepository repository; // 构造器注入 @Override public void run(ApplicationArguments args) { repository.migrateLegacyData(); // 安全使用Bean } } ``` > ✅ **最佳场景**:启动时数据迁移或外部资源初始化[^2][^4] --- ### 三、底层扩展点(谨慎使用) 5. **`BeanPostProcessor` 后置处理器** 在**每个Bean初始化前后**拦截: ```java import org.springframework.beans.factory.config.BeanPostProcessor; @Component public class ValidationProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String name) { if(bean instanceof Validatable) { ((Validatable)bean).validate(); // 验证Bean状态 } return bean; } } ``` > ⚠️ **注意**:需避免性能问题,仅用于特定Bean[^1] --- ### 执行时序总结 | 阶段 | 方法 | 触发时机 | |------|------|----------| | 1 | `@PostConstruct` | 当前Bean注入完成后 | | 2 | `BeanPostProcessor` | 每个Bean初始化前后 | | 3 | `SmartInitializingSingleton` | 所有单例Bean实例化后 | | 4 | `ContextRefreshedEvent` | 容器完全刷新时 | | 5 | `ApplicationRunner` | 应用启动完成时 | > 关键原则:**越靠近容器底层的扩展点执行越早,但使用复杂度越高**。常规需求优先选择`@PostConstruct`或`ApplicationRunner`[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值