Springboot 启动时Bean的创建与注入(一)-面试热点-springboot源码解读-xunznux

Springboot 启动时Bean的创建与注入,以及对应的源码解读

文章目录

    • Springboot 启动时Bean的创建与注入,以及对应的源码解读
    • 构建Web项目流程图:
    • 堆栈信息:
    • 堆栈信息简介
    • 堆栈信息源码详解
      • 1、`main:10, DemoApplication (com.xun.demo)`
      • 2、`run:1352, SpringApplication (org.springframework.boot)`
      • 3、run:1363, SpringApplication (org.springframework.boot)
      • 4、run:335, SpringApplication (org.springframework.boot)
      • 5、refreshContext:456, SpringApplication (org.springframework.boot)
      • 6、refresh:754, SpringApplication (org.springframework.boot)
      • 7、refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
      • 8、refresh:624, AbstractApplicationContext (org.springframework.context.support)
      • 9、finishBeanFactoryInitialization:962, AbstractApplicationContext (org.springframework.context.support)
      • 10、preInstantiateSingletons:975, DefaultListableBeanFactory (org.springframework.beans.factory.support)
    • 其他内容

出于研究springboot启动时bean的创建到注入中发生的函数调用的这个目的,搭建一个最简单的springboot应用,只有一个controller bean,没有再手动指定任何其他bean。
先展示一下如何在idea中快速构建一个springboot web demo项目:

构建Web项目流程图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里推荐一个插件,可以在插件市场中搜索 Maven Search安装,用于寻找依赖:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了得到堆栈调用信息,需要找到InjectionMetadata 类中的 inject 方法打断点调试:
在这里插入图片描述

堆栈信息:

inject:142, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessProperties:508, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1421, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:599, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:522, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:337, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, AbstractBeanFactory$$Lambda$330/0x0000020b811ebab8 (org.springframework.beans.factory.support)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:975, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:962, AbstractApplicationContext (org.springframework.context.support)
refresh:624, AbstractApplicationContext (org.springframework.context.support)
refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:754, SpringApplication (org.springframework.boot)
refreshContext:456, SpringApplication (org.springframework.boot)
run:335, SpringApplication (org.springframework.boot)
run:1363, SpringApplication (org.springframework.boot)
run:1352, SpringApplication (org.springframework.boot)
main:10, DemoApplication (com.xun.demo)

堆栈信息简介

这个堆栈信息展示了 Spring Boot 在启动过程中如何创建和注入 bean 的详细过程。下面是对每个函数调用的简单解释,详细代码解释在后面进行:

  1. inject:142, InjectionMetadata (org.springframework.beans.factory.annotation)
    InjectionMetadata 是一个 Spring 类,用于处理注入点元数据。inject 方法负责实际的注入操作,根据注解(如 @Autowired)将依赖注入到目标 bean 中。

  2. postProcessProperties:508, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    AutowiredAnnotationBeanPostProcessor 是一个 BeanPostProcessor,负责处理 @Autowired@Value 注解。postProcessProperties 方法在 Spring 容器实例化 bean 之后但在它们的依赖项被注入之前被调用,用于处理依赖注入。

  3. populateBean:1421, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    populateBean 方法用于填充给定 bean 实例的属性。这包括对 @Autowired@Value 等注解的处理,并将相应的依赖注入到 bean 中。

  4. doCreateBean:599, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    doCreateBean 方法负责实际创建一个新的 bean 实例,包括调用其构造函数、应用工厂后处理器、自动装配和初始化。

  5. createBean:522, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    createBean 方法是 doCreateBean 的高层次包装器,负责创建并初始化一个新的 bean 实例。

  6. lambda$doGetBean$0:337, AbstractBeanFactory (org.springframework.beans.factory.support)
    这是一个 lambda 表达式,作为回调传递给 getSingleton 方法,负责返回一个单例 bean 实例。

  7. getObject:-1, AbstractBeanFactory$$Lambda$330/0x0000020b811ebab8 (org.springframework.beans.factory.support)
    这是 lambda 表达式的实际实现,用于从单例注册表中获取或创建一个单例 bean。

  8. getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
    getSingleton 方法从单例缓存中获取 bean 实例,如果 bean 不存在,则创建并缓存它。

  9. doGetBean:335, AbstractBeanFactory (org.springframework.beans.factory.support)
    doGetBean 方法负责实际获取一个 bean 实例,包括处理依赖注入和初始化。

  10. getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)
    getBean 方法是 doGetBean 的高层次包装器,用于获取一个 bean 实例。

  11. preInstantiateSingletons:975, DefaultListableBeanFactory (org.springframework.beans.factory.support)
    preInstantiateSingletons 方法负责提前实例化所有非延迟加载的单例 bean,确保它们在应用启动时就被创建和初始化。

  12. finishBeanFactoryInitialization:962, AbstractApplicationContext (org.springframework.context.support)
    finishBeanFactoryInitialization 方法在 Spring 容器刷新期间被调用,完成 bean 工厂的初始化,包括实例化所有剩余的单例 bean。

  13. refresh:624, AbstractApplicationContext (org.springframework.context.support)
    refresh 方法是 Spring 应用上下文刷新逻辑的入口点,负责重新加载 bean 定义、初始化 Spring 环境和 bean 工厂。

  14. refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
    这是 ServletWebServerApplicationContext 类中 refresh 方法的实现,用于刷新 Web 应用上下文。

  15. refresh:754, SpringApplication (org.springframework.boot)
    refresh 方法是 Spring Boot 应用启动过程中刷新应用上下文的关键步骤。

  16. refreshContext:456, SpringApplication (org.springframework.boot)
    refreshContext 方法用于刷新 Spring 应用上下文,确保所有 bean 定义和依赖关系都被正确加载和初始化。

  17. run:335, SpringApplication (org.springframework.boot)
    run 方法是 Spring Boot 应用的主要入口点,负责启动 Spring 应用上下文。

  18. run:1363, SpringApplication (org.springframework.boot)
    这是 run 方法的一种签名,通常用于启动 Spring Boot 应用。

  19. run:1352, SpringApplication (org.springframework.boot)
    这是 run 方法的一种签名,通常用于启动 Spring Boot 应用。

  20. main:10, DemoApplication (com.xun.demo)
    这是应用的主类 DemoApplicationmain 方法,是 Spring Boot 应用的入口点。

通过这些方法调用,Spring Boot 应用在启动过程中创建和注入 bean,确保所有依赖关系都被正确解析和注入。

堆栈信息源码详解

先从栈底部的main方法开始看起。
Springboot 启动时Bean的创建与注入(二)-面试热点-springboot源码解读-xunznux
Springboot 的Bean生命周期五步、七步、十步详解以及框架源码解读

1、main:10, DemoApplication (com.xun.demo)

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

这个代码是一个典型的 Spring Boot 应用程序的入口类,下面是对每个部分的详细解释:

  1. @SpringBootApplication
    这个注解是一个组合注解,它结合了以下三个常用的 Spring 注解:
  • @SpringBootConfiguration: 表示这是一个 Spring Boot 配置类,等同于 Spring 的 @Configuration 注解。
  • @EnableAutoConfiguration: 这个注解告诉 Spring Boot 根据添加的依赖自动配置 Spring 应用程序。
  • @ComponentScan: 这个注解启用组件扫描,让 Spring 找到并注册带有 @Component@Service@Repository@Controller 等注解的类。
  1. public class DemoApplication
    这是 Spring Boot 应用的主类,命名为 DemoApplication。按照惯例,Spring Boot 应用的主类通常位于源码目录的根包下,以便能够扫描到该包及其子包中的所有组件。

  2. public static void main(String[] args)
    这是 Java 应用程序的入口点。main 方法是应用程序启动时第一个执行的方法。

  3. SpringApplication.run(DemoApplication.class, args)
    这个静态方法启动了 Spring Boot 应用程序。以下是一些关键点:

  • SpringApplication.run 方法负责启动 Spring 应用上下文,启动内嵌的服务器(例如 Tomcat),并初始化 Spring 环境。
  • DemoApplication.class 是应用的主类,它包含 @SpringBootApplication 注解,使其成为 Spring Boot 应用的配置类。
  • args 是传递给应用的命令行参数。

SpringApplication.run 被调用时,Spring Boot 会执行以下步骤:

  1. 创建并启动 SpringApplication 实例。
  2. 准备 SpringApplication 实例(例如,读取并解析命令行参数)。
  3. 创建并刷新应用上下文(包括创建所有单例 bean)。
  4. 启动嵌入式服务器(例如 Tomcat)。
  5. 执行所有 CommandLineRunnerApplicationRunner bean。

这个简单的 Spring Boot 应用程序入口类通过 @SpringBootApplication 注解和 SpringApplication.run 方法启动了一个完整的 Spring Boot 应用,包含自动配置和内嵌服务器,简化了 Spring 应用的开发和部署。

2、run:1352, SpringApplication (org.springframework.boot)

/*** Static helper that can be used to run a {@link SpringApplication} from the* specified source using default settings.* @param primarySource the primary source to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
}
  • 这是一个静态辅助方法,可以使用默认设置从指定的源运行一个 SpringApplication。
  • 参数 primarySource 是要加载的主要源,通常是一个带有 @SpringBootApplication 注解的主类。
  • 参数 args 是应用程序的参数,通常是从 Java 主方法传递的命令行参数。
  • 返回正在运行的 ApplicationContext,这是 Spring 应用程序上下文的表示。

这个静态 run 方法是启动 Spring Boot 应用程序的便捷方法。它接受一个主要源类和应用程序参数,将主要源类包装成一个数组,然后调用另一个 run 方法来启动应用程序。最终返回一个 ConfigurableApplicationContext,表示应用程序的运行上下文。

3、run:1363, SpringApplication (org.springframework.boot)

/*** Static helper that can be used to run a {@link SpringApplication} from the* specified sources using default settings and user supplied arguments.* @param primarySources the primary sources to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}

这个静态 run 方法是启动 Spring Boot 应用程序的便捷方法。它接受一个主要源类数组和应用程序参数,创建一个 SpringApplication 实例,并调用它的 run 方法来启动应用程序。最终返回一个 ConfigurableApplicationContext,表示应用程序的运行上下文。

4、run:335, SpringApplication (org.springframework.boot)

/*** Run the Spring application, creating and refreshing a new {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/
public ConfigurableApplicationContext run(String... args) {// 创建一个用于记录启动时间和事件的 Startup 实例Startup startup = Startup.create();// 如果启用了 registerShutdownHook,注册一个 JVM 关闭钩子if (this.registerShutdownHook) {SpringApplication.shutdownHook.enableShutdownHookAddition();}// 创建一个 DefaultBootstrapContext 实例,用于引导应用程序上下文的初始化DefaultBootstrapContext bootstrapContext = createBootstrapContext();// 声明一个 ConfigurableApplicationContext 变量ConfigurableApplicationContext context = null;// 配置 headless 属性,防止某些图形环境相关的问题configureHeadlessProperty();// 获取应用程序运行的监听器,用于在不同阶段触发事件SpringApplicationRunListeners listeners = getRunListeners(args);// 通知监听器应用程序正在启动listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 创建一个 ApplicationArguments 实例,解析命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备并配置环境变量ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 打印启动横幅Banner printedBanner = printBanner(environment);// 创建应用程序上下文context = createApplicationContext();// 设置应用程序启动器context.setApplicationStartup(this.applicationStartup);// 准备应用程序上下文,配置相关属性和资源prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 刷新应用程序上下文,加载所有 Bean 定义并启动应用程序refreshContext(context);// 在上下文刷新后进行后续操作afterRefresh(context, applicationArguments);// 记录启动时间startup.started();// 如果 logStartupInfo 为 true,记录启动信息if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);}// 通知监听器应用程序已启动listeners.started(context, startup.timeTakenToStarted());// 调用所有 CommandLineRunner 和 ApplicationRunner 实现类callRunners(context, applicationArguments);}catch (Throwable ex) {// 捕获异常并处理启动失败的情况throw handleRunFailure(context, ex, listeners);}try {// 如果上下文正在运行,通知监听器应用程序已就绪if (context.isRunning()) {listeners.ready(context, startup.ready());}}catch (Throwable ex) {// 捕获异常并处理启动失败的情况throw handleRunFailure(context, ex, null);}// 返回运行中的 ConfigurableApplicationContext 实例return context;
}

这个 run 方法执行了 Spring Boot 应用程序的完整启动过程,从准备环境、创建应用程序上下文到通知监听器和调用运行器。通过多个步骤,确保应用程序在启动过程中正确配置和初始化。最终返回一个正在运行的 ApplicationContext 实例。
该方法运行一个 Spring 应用程序,创建并刷新一个新的 ApplicationContext 实例。ApplicationContext 是 Spring 框架中的核心接口之一,代表了 Spring 的 IoC 容器,用于管理 Spring 应用中的 beans。
以下是 run 方法的主要流程和作用:

  1. 创建启动记录实例: 创建一个 Startup 实例,用于记录应用启动的时间和事件。

  2. 注册 JVM 关闭钩子: 如果配置了 registerShutdownHook,则注册一个 JVM 关闭钩子,以确保在 JVM 关闭时能正确地进行清理工作。

  3. 创建引导上下文: 创建一个 DefaultBootstrapContext 实例,用于在应用上下文初始化期间保存引导信息。

  4. 配置 headless 属性: 配置 headless 属性,以防止在没有显示器环境中运行时的一些问题。

  5. 获取运行监听器: 获取 SpringApplicationRunListeners 实例,用于在不同的应用启动阶段触发事件。

  6. 通知监听器应用程序正在启动: 通知监听器应用程序启动的开始。

  7. 解析命令行参数: 创建 ApplicationArguments 实例,解析传入的命令行参数。

  8. 准备并配置环境: 准备并配置 Spring 的环境对象。

  9. 打印启动横幅: 根据环境配置打印应用启动横幅。

  10. 创建应用上下文: 创建 ApplicationContext 实例,用于管理 Spring beans。

  11. 设置应用启动器: 将应用启动器设置到上下文中。

  12. 准备应用上下文: 准备应用上下文,包括加载 bean 定义等。

  13. 刷新应用上下文: 刷新应用上下文,实际启动 Spring 容器。

  14. 执行刷新后操作: 在上下文刷新后执行一些必要的操作。

  15. 记录启动时间: 记录应用程序启动的时间。

  16. 记录启动信息: 如果启用了启动信息日志,则记录启动信息。

  17. 通知监听器应用程序已启动: 通知监听器应用程序已经启动完成。

  18. 调用命令行运行器: 调用所有实现 CommandLineRunnerApplicationRunner 接口的类。

  19. 处理启动失败: 捕获并处理启动过程中出现的异常。

  20. 通知监听器应用程序已就绪: 通知监听器应用程序已经准备就绪,可以处理请求。

  21. 返回应用上下文: 返回运行中的 ConfigurableApplicationContext 实例。

5、refreshContext:456, SpringApplication (org.springframework.boot)

private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {shutdownHook.registerApplicationContext(context);}refresh(context);
}
  • 注册关闭钩子:如果配置了 registerShutdownHook,则调用 shutdownHook 的 registerApplicationContext 方法,注册应用上下文以便在 JVM 关闭时执行清理操作。
  • 刷新应用上下文:调用 refresh(context) 方法,这个方法是 AbstractApplicationContext 中定义的抽象方法,用于刷新应用上下文,包括初始化所有单例 beans。

这个过程确保应用上下文在启动时被正确初始化和配置,并且所有必要的单例 beans 被实例化和装配。

6、refresh:754, SpringApplication (org.springframework.boot)

/*** Refresh the underlying {@link ApplicationContext}.* @param applicationContext the application context to refresh*/
protected void refresh(ConfigurableApplicationContext applicationContext) {// 调用 ApplicationContext 的 refresh() 方法来执行刷新操作applicationContext.refresh();
}

方法用于执行给定应用上下文的刷新操作,确保所有的配置和 beans 都是最新的。

7、refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)

/*** 覆盖父类的 refresh() 方法,执行应用上下文的刷新操作。* @throws BeansException 如果 bean 创建或初始化过程中出现问题* @throws IllegalStateException 如果应用上下文已经刷新过或处于不合法状态*/
@Override
public final void refresh() throws BeansException, IllegalStateException {try {// 调用父类的 refresh() 方法,执行应用上下文的刷新操作super.refresh();}catch (RuntimeException ex) {// 如果在刷新过程中出现运行时异常,则捕获异常并处理WebServer webServer = this.webServer;if (webServer != null) {// 如果存在 WebServer,停止并销毁它webServer.stop();webServer.destroy();}// 抛出捕获到的运行时异常,向上层传递异常信息throw ex;}
}

这段代码中的 refresh() 方法是 ApplicationContext 接口的实现方法,在 Spring 应用上下文初始化后被调用,用于执行上下文的刷新操作。

8、refresh:624, AbstractApplicationContext (org.springframework.context.support)

 /*** 加载或刷新配置的持久表示,该配置可能来自基于Java的配置、XML文件、属性文件、* 关系数据库模式或其他某种格式。* <p>* 由于这是一个启动方法,如果失败,它应该销毁已创建的单例,* 以避免悬挂资源。换句话说,在调用此方法之后,要么所有单例都被实例化,* 要么根本没有单例被实例化。* * @throws BeansException 如果bean工厂无法初始化* @throws IllegalStateException 如果已经初始化并且不支持多次刷新尝试*/
@Override
public void refresh() throws BeansException, IllegalStateException {// 获取锁以确保刷新过程的线程安全this.startupShutdownLock.lock();try {// 记录当前线程作为执行刷新操作的线程this.startupShutdownThread = Thread.currentThread();// 开始一个启动步骤,用于上下文刷新过程StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// 准备刷新过程prepareRefresh();// 通知子类刷新内部bean工厂// 获得一个新的bean工厂实例ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 为这个上下文准备bean工厂prepareBeanFactory(beanFactory);try {// 允许上下文子类对bean工厂进行后处理postProcessBeanFactory(beanFactory);// 开始一个启动步骤,用于bean后处理StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// 调用注册在上下文中的bean工厂后处理器invokeBeanFactoryPostProcessors(beanFactory);// 注册拦截bean创建的bean后处理器registerBeanPostProcessors(beanFactory);// 结束bean后处理的启动步骤beanPostProcess.end();// 初始化消息源initMessageSource();// 初始化事件多播器initApplicationEventMulticaster();// 在特定的上下文子类中初始化其他特殊beanonRefresh();// 检查监听器bean并注册它们registerListeners();// 实例化所有剩余(非懒加载初始化)的单例finishBeanFactoryInitialization(beanFactory);// 完成刷新过程finishRefresh();}// 捕获任何运行时异常或错误catch (RuntimeException | Error ex) {// 如果警告日志启用,记录异常并取消刷新尝试if (logger.isWarnEnabled()) {logger.warn("在上下文初始化过程中遇到异常 - 取消刷新尝试: " + ex);}// 销毁已创建的单例以避免资源泄露(如果失败,应该销毁已创建的单例)destroyBeans();// 重置'活动'标志cancelRefresh(ex);// 将异常传播给调用者throw ex;}// 确保结束上下文刷新的启动步骤finally {contextRefresh.end();}}// 无论刷新成功还是失败,都释放锁finally {this.startupShutdownThread = null;this.startupShutdownLock.unlock();}
}

该方法是 AbstractApplicationContext 类中的 refresh() 方法的实现,用于刷新 Spring 应用上下文的整个流程。以下是其详细流程总结:

  1. 获取启动和关闭锁: 获取 startupShutdownLock 锁,确保在应用上下文刷新期间不会被其他线程中断。
  2. 设置当前线程为启动/关闭线程: 将当前线程设置为 startupShutdownThread,以便在需要时能够进行相应处理。
  3. 应用启动步骤记录: 使用 applicationStartup 记录应用启动步骤,标记为 “spring.context.refresh”。用于记录上下文刷新过程的性能指标。
  4. 准备刷新操作: 调用 prepareRefresh() 方法,为刷新操作做准备。包括设置必要的环境属性和初始化一些状态。
  5. 获取新的 Bean 工厂: 调用 obtainFreshBeanFactory() 方法获取一个新的 ConfigurableListableBeanFactory 实例。通常是通过重新加载Bean定义来实现的。
  6. 为使用准备 Bean 工厂: 调用 prepareBeanFactory(beanFactory) 方法,为当前上下文的使用准备 Bean 工厂。包括设置类加载器、注册默认的环境Bean等。
  7. 后处理 Bean 工厂: 调用 postProcessBeanFactory(beanFactory) 方法,允许子类对 Bean 工厂进行后处理。
  8. 调用 Bean 工厂后处理器: 调用 invokeBeanFactoryPostProcessors(beanFactory) 方法,执行注册在上下文中的 Bean 工厂后处理器。
  9. 注册 Bean 后处理器: 调用 registerBeanPostProcessors(beanFactory) 方法,注册拦截 Bean 创建的 Bean 后处理器。
  10. 初始化消息源: 调用 initMessageSource() 方法,初始化该上下文的消息源。
  11. 初始化应用事件广播器: 调用 initApplicationEventMulticaster() 方法,初始化该上下文的应用事件广播器。用于发布和监听应用事件。
  12. 特定上下文子类的初始化: 调用 onRefresh() 方法,初始化特定于该上下文子类的其他特殊 Bean。
  13. 注册监听器: 调用 registerListeners() 方法,检查并注册监听器 Bean。
  14. 完成 Bean 工厂的初始化: 调用 finishBeanFactoryInitialization(beanFactory) 方法,实例化所有剩余的非延迟初始化单例 Bean。
  15. 完成刷新操作: 调用 finishRefresh() 方法,完成应用上下文的刷新。
  16. 处理异常: 在执行上述步骤的任何过程中,如果捕获到 RuntimeExceptionError,则进入异常处理块。
  • 记录警告日志,取消刷新尝试。
  • 销毁已创建的单例 Bean,避免资源泄漏。
  • 设置 active 标志为 false。
  • 向上层抛出捕获到的异常。
  1. 结束应用启动步骤记录: 无论是否发生异常,都会调用 contextRefresh.end() 结束应用启动步骤记录。
  2. 释放启动和关闭锁: 最后在 finally 块中释放 startupShutdownThread,解锁 startupShutdownLock,确保安全地结束刷新过程。

这个方法的主要目的是确保 Spring 应用上下文能够在启动时进行正确的初始化和配置,包括加载配置、注册 Bean、初始化消息源和事件广播器等重要步骤,最终保证应用程序能够正常运行。

9、finishBeanFactoryInitialization:962, AbstractApplicationContext (org.springframework.context.support)

/*** 完成本上下文的bean工厂初始化,初始化所有剩余的单例bean。* <p>* 此方法执行以下操作:* 1. 初始化此上下文的转换服务,如果存在名为CONVERSION_SERVICE_BEAN_NAME的bean且类型为ConversionService。* 2. 如果没有BeanFactoryPostProcessor注册过内嵌的值解析器,则注册一个默认的环境占位符解析器。* 3. 早期初始化LoadTimeWeaverAware类型的beans,以便于尽早注册它们的transformer。* 4. 停止使用临时类加载器进行类型匹配。* 5. 冻结bean定义元数据,不再期望进一步的变化。* 6. 实例化所有剩余的(非懒加载初始化)单例。* * @param beanFactory 当前的可配置列表bean工厂*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// 如果存在转换服务bean,初始化转换服务(为此上下文初始化转换服务)if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {// 设置bean工厂的转换服务beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// 如果没有任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean)// 之前注册过嵌入值解析器,则注册一个默认的嵌入值解析器:// 此时,主要用于解析注解属性值中的占位符。if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));}// 提前初始化 LoadTimeWeaverAware beans,以便及早注册它们的转换器 transformer。String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {beanFactory.getBean(weaverAwareName, LoadTimeWeaverAware.class);}// 停止使用临时类加载器进行类型匹配beanFactory.setTempClassLoader(null);// 冻结配置,允许缓存所有的Bean定义元数据,不再期望有进一步的更改。beanFactory.freezeConfiguration();// 实例化所有剩余的(非懒加载初始化)单例 BeanbeanFactory.preInstantiateSingletons();
}

这个方法的主要作用是完成Spring应用上下文中Bean工厂的初始化过程,特别是初始化所有剩余的单例Bean。具体来说,它执行了一系列步骤来确保Bean工厂的配置和Bean实例的准备工作都已完成。以下是每个步骤的详细解释:

  • 初始化转换服务:检查是否存在名为CONVERSION_SERVICE_BEAN_NAME的Bean,并且该Bean的类型匹配ConversionService。如果存在,则将其设置为Bean工厂的转换服务。这一步确保了在Bean属性转换时使用正确的转换服务。
  • 注册默认嵌入值解析器:如果没有嵌入值解析器,则添加一个默认的解析器,用于解析注解属性值中的占位符。这一步确保了注解中的占位符能够被正确解析。
  • 提前初始化LoadTimeWeaverAware bean:获取所有实现了LoadTimeWeaverAware接口的Bean名称,并提前初始化这些Bean,以便及早注册它们的类加载时转换器。这一步确保了类加载时的增强功能能够正确注册。
  • 停止使用临时ClassLoader:停止使用临时的ClassLoader进行类型匹配。这一步通常是在Bean定义解析和类型匹配完成后进行的清理工作。
  • 冻结Bean定义元数据:冻结Bean定义元数据,表示不再期望有进一步的更改。这一步确保了Bean定义的稳定性和一致性。
  • 实例化所有剩余的单例Bean:实例化所有剩余的(非懒加载的)单例Bean。这一步确保了所有需要立即初始化的单例Bean都已创建并准备就绪。

总结:这个方法的整体作用是**确保Spring应用上下文中的Bean工厂已经完全初始化,并且所有需要的单例Bean都已实例化。**它通过一系列步骤来配置转换服务、解析占位符、注册类加载时转换器、清理临时资源、冻结配置并最终实例化所有单例Bean,从而确保应用上下文的稳定性和一致性。

10、preInstantiateSingletons:975, DefaultListableBeanFactory (org.springframework.beans.factory.support)

/*** 确保所有非懒加载的单例Bean都被实例化,同时考虑{@link org.springframework.beans.factory.FactoryBean FactoryBeans}。* 通常在工厂设置结束时调用(如果需要)。* @throws BeansException 如果某个单例Bean无法创建。* 注意:这可能会导致工厂中已经初始化了一些Bean!* 在这种情况下,请调用{@link #destroySingletons()}进行完全清理。* @see #destroySingletons()*/
@Override
public void preInstantiateSingletons() throws BeansException {// 如果日志级别为TRACE,则记录预实例化单例Bean的操作。if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// 迭代一个Bean定义名称的副本,以允许在初始化方法中注册新的Bean定义。// 虽然这可能不是常规工厂引导的一部分,但在其他情况下工作正常。List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// 触发所有非懒加载单例Bean的初始化...for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 检查Bean定义是否不是抽象的、是单例的并且不是懒加载的。if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 如果是FactoryBean,则获取FactoryBean实例。if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);// 如果是SmartFactoryBean并且需要急切初始化,则获取Bean实例。if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {getBean(beanName);}}else {// 否则,直接获取Bean实例。getBean(beanName);}}}// 触发所有适用Bean的后初始化回调...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);// 如果Bean实例是SmartInitializingSingleton,则调用其afterSingletonsInstantiated方法。if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);smartSingleton.afterSingletonsInstantiated();smartInitialize.end();}}
}

这段代码是 DefaultListableBeanFactory 类中的 preInstantiateSingletons 方法,用于预实例化所有非延迟初始化的单例 Beans,同时考虑到 FactoryBean主要作用是预实例化Spring应用上下文中的所有非懒加载单例Bean,并在所有单例Bean实例化后触发相应的回调方法。通过预实例化,可以确保所有必要的Bean在应用启动时已经准备就绪,从而提高应用的响应速度和稳定性。

以下是方法的流程总结:

  1. 日志记录

    • 如果日志级别为 trace,则记录日志,指示正在预实例化单例 Beans。
  2. 复制 Bean 名称列表

    • 创建一个 beanNames 列表,复制当前 Bean 工厂中所有的 Bean 名称。这样做是为了允许在初始化方法期间可能注册新的 Bean 定义。(可以安全地在初始化过程中注册新的bean定义)
  3. 实例化所有非延迟初始化的单例 Beans

    • 遍历 beanNames 列表中的每个 Bean 名称。
    • 获取与当前 Bean 名称关联的合并的 RootBeanDefinition。
    • 检查该 Bean 定义是否为非抽象、单例且非延迟初始化。
    • 如果是工厂Bean FactoryBean,则首先获取其对应的 FactoryBean 实例,如果是 SmartFactoryBean 并且设置为 eagerInit(需要急切初始化),则立即获取其实例。
    • 否则,直接通过 getBean(beanName) 方法获取该 Bean 的实例。
  4. 触发所有适用 Bean 的后初始化回调

    • 再次遍历 beanNames 列表中的每个 Bean 名称。
    • 获取每个 Bean 名称对应的单例实例。
    • 如果该实例实现了 SmartInitializingSingleton 接口,调用其 afterSingletonsInstantiated() 方法,执行后初始化逻辑。这是一个回调方法,用于在所有单例Bean实例化后执行一些自定义逻辑。

preInstantiateSingletons方法的**主要作用是确保所有非懒加载的单例beans在应用上下文完全启动之前已经被实例化和初始化。**这一步骤对于依赖于这些beans的其他组件来说至关重要,因为它保证了这些beans的可用性。同时,它还提供了对那些希望在所有单例beans实例化后执行某些操作的beans的支持,通过调用SmartInitializingSingleton接口的afterSingletonsInstantiated方法。
总之,preInstantiateSingletons方法是Spring框架上下文启动流程中的一个关键环节,它负责最终确定所有非懒加载单例beans的状态,为整个应用提供了一个完整且一致的运行时环境。

注:由于内容过长,剩余的核心方法解释放在下一篇文章中。
Springboot 启动时Bean的创建与注入(二)-面试热点-springboot源码解读-xunznux
Springboot 的Bean生命周期五步、七步、十步详解以及框架源码解读

其他内容

如果对 Golang 实现的Raft共识算法、Golang实现的负载均衡或者 Golang web 项目的demo实现感兴趣的可以看看我的主页,各个部分都带有详细的代码解释,也可以通过代码仓库获取源码,直接运行。
gitee链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/874181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HashMap与ConcurrentHashMap

文章目录 HashMap1.1 HashMap 的数据结构&#xff1f;1.2 HashMap 的动态扩容1.3 Hash实现方法1.4 如何解决Hash冲突 ConcurrentHashMap HashMap 1.1 HashMap 的数据结构&#xff1f; 哈希表结构&#xff08;链表散列&#xff1a;数组链表&#xff09;实现&#xff0c;结合数…

详细分析Springboot自定义启动界面(附Demo)

目录 前言1. banner.text1.1 配置文件关闭1.2 启动类关闭1.3 命令行关闭 2. 自定义Banner类3. 自动配置类4. 总结 前言 实现自定义启动动画是一项有趣的任务&#xff0c;虽然Spring Boot本身不提供内置的动画功能&#xff0c;但可以通过一些技巧来实现 以下主要以Demo的形式展…

三字棋游戏(C语言详细解释)

hello&#xff0c;小伙伴们大家好&#xff0c;算是失踪人口回归了哈&#xff0c;主要原因是期末考试完学校组织实训&#xff0c;做了俄罗斯方块&#xff0c;后续也会更新&#xff0c;不过今天先从简单的三字棋说起 话不多说&#xff0c;开始今天的内容 一、大体思路 我们都知…

MongoDB教程(十三):MongoDB覆盖索引

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言什么是覆盖…

数据结构(栈及其实现)

栈 概念与结构 栈&#xff1a;⼀种特殊的线性表&#xff0c;其只允许在固定的⼀端进⾏插⼊和删除元素操作。 进⾏数据插⼊和删除操作的⼀端称为栈顶&#xff0c;另⼀端称为栈底。栈中的数据元素遵守后进先出 LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&…

PyCharm创建一个空的python项目

1.设置项目路径 2.配置python解释器 右下角可以选择always

【Linux】线程——生产者消费者模型、基于阻塞队列的生产消费者模型、基于环形队列的生产消费者模型、POSIX信号量的概念和使用

文章目录 Linux线程6. 生产消费者模型6.1 基于阻塞队列的生产消费者模型6.1.1 阻塞队列模型实现 6.2 基于环形队列的生产消费者模型6.2.1 POSIX信号量的概念6.2.2 POSIX信号量的使用6.2.3 环形队列模型实现 Linux线程 6. 生产消费者模型 生产消费者模型的概念 生产者消费者模…

Jackson详解

文章目录 一、Jackson介绍二、基础序列化和反序列化1、快速入门2、序列化API3、反序列化API4、常用配置 三、常用注解1、JsonProperty2、JsonAlias3、JsonIgnore4、JsonIgnoreProperties5、JsonFormat6、JsonPropertyOrder 四、高级特性1、处理泛型1.1、反序列化List泛型1.2、反…

Java 写一个可以持续发送消息的socket服务端

前言 最近在学习flink, 为了模仿一个持续的无界的数据源, 所以需要一个可以持续发送消息的socket服务端. 先上效果图 效果图 socket服务端可以持续的发送消息, flink端是一个统计单词出现总数的消费端,效果图如下 源代码 flink的消费端就不展示了, 需要引入一些依赖和版本…

Linux系统编程基础

Linux操作系统 Linux不是一个具体的操作系统&#xff0c;而是一类操作系统的总称&#xff0c;具体版本成为发行版。 Red Hat&#xff1a;目前被IBM收购&#xff0c;收费版&#xff0c;目前最大的Linux供应商CentOS&#xff1a; Red Hat退出的免费版Ubuntu&#xff1a;界面比较友…

二十一、【机器学习】【非监督学习】- 谱聚类 (Spectral Clustering)​​

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

hung 之 Android llkd

目录 1. llkd 简介 2. 原理 2.1 内核活锁 2.2 检测机制 2.3 为什么 persistent stack signature 检测机制不执行 ABA 检查&#xff1f; 2.4 为什么 kill 进程后&#xff0c;进程还存在就能判定发生了内核 live-lock&#xff1f; 3. 代码 3.1 内核 live-lock 检查 3.2 …

摸鱼大数据——用户画像——如何给用户“画像”

2、如何给用户“画像” 2.1 什么是标签体系 标签: 是某一种用户特征的符号表示 标签体系: 把用户分到多少类别里面去, 这些类是什么, 彼此之间有什么关系, 就构成了标签体系 标签解决的问题: 解决描述(或命名)问题以及解决数据之间的关联 2.2.1 标签的分类 用户画像标签一…

【附源码】IMX6U嵌入式Linux开发板连接阿里云--MQTT协议

演示 IMX6U嵌入式Linux开发板连接阿里云 阿里云创建设备&&获取LinkSDK 如果还不知道怎么在阿里云创建设备和获取连接阿里云的LinkSDK的话&#xff0c;先看这篇文章&#xff0c;再到这里。看这篇文章的时候&#xff0c;麻烦将下方文章打开对照着看&#xff0c;因为一些…

重测序数据处理得到vcf文件

重测序数据处理得到vcf文件 文章目录 重测序数据处理前言1. 数据是rawdata&#xff0c;需用fastp对数据进行质控和过滤2. 利用getorganelle软件组装叶绿体基因组3. 检查基因组大小&#xff0c;确认是否完整&#xff0c;然后和已知的红毛菜科叶绿体基因组一起构树4. 根据树形结果…

微积分-微分应用2(平均值定理)

要得出平均值定理&#xff0c;我们首先需要以下结果。 罗尔定理 设函数 f f f 满足以下三个假设&#xff1a; f f f 在闭区间 [ a , b ] [a, b] [a,b] 上连续。 f f f 在开区间 ( a , b ) (a, b) (a,b) 上可导。 f ( a ) f ( b ) f(a) f(b) f(a)f(b) 则在开区间 ( a , b …

CTFHUB-SQL注入-UA注入

目录 判断是否存在注入 判断字段数量 判断回显位置 查询数据库名 查询数据库下的表名 查询表中的字段名 查询字段名下的数据 由于本关是UA注入&#xff0c;就不浪费时间判断是什么注入了&#xff0c;在该页面使用 burp工具 抓包&#xff0c;修改User-Agent&#xff0c;加…

JavaScript之Web APIs-DOM

目录 DOM获取元素一、Web API 基本认知1.1 变量声明1.2 作用和分类1.3 DOM树1.4 DOM对象 二、获取DOM对象2.1 通过CSS选择器来获取DOM元素2.2 通过其他方式来获取DOM元素 三、操作元素内容3.1 元素.innerTest属性3.2 元素.innerHTML属性 四、操作元素属性4.1 操作元素常用属性4…

图形编辑器基于Paper.js教程09:鼠标拖动画布,以鼠标点为缩放中心进行视图的缩放

如何使用Paper.js实现画布的缩放与拖动功能 在Web开发中&#xff0c;利用Paper.js库进行图形的绘制和交互操作是一种常见的实践。Paper.js是一个强大的矢量图形库&#xff0c;可以让开发者通过简洁的API完成复杂的图形操作。在本文中&#xff0c;我们将详细探讨如何使用Paper.…

昇思25天学习打卡营第29天 | 基于MindSpore通过GPT实现情感分类

基于MindSpore框架通过GPT模型实现情感分类展示了从项目设置、数据预处理到模型训练和评估的详细步骤&#xff0c;提供了一个完整的案例来理解如何在自然语言处理任务中实现情感分析。 首先&#xff0c;环境配置是任何机器学习项目的起点。项目通过安装特定版本的MindSpore和相…