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 的详细过程。下面是对每个函数调用的简单解释,详细代码解释在后面进行:
-
inject:142, InjectionMetadata (org.springframework.beans.factory.annotation)
InjectionMetadata
是一个 Spring 类,用于处理注入点元数据。inject
方法负责实际的注入操作,根据注解(如@Autowired
)将依赖注入到目标 bean 中。 -
postProcessProperties:508, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
AutowiredAnnotationBeanPostProcessor
是一个 BeanPostProcessor,负责处理@Autowired
和@Value
注解。postProcessProperties
方法在 Spring 容器实例化 bean 之后但在它们的依赖项被注入之前被调用,用于处理依赖注入。 -
populateBean:1421, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
populateBean
方法用于填充给定 bean 实例的属性。这包括对@Autowired
、@Value
等注解的处理,并将相应的依赖注入到 bean 中。 -
doCreateBean:599, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean
方法负责实际创建一个新的 bean 实例,包括调用其构造函数、应用工厂后处理器、自动装配和初始化。 -
createBean:522, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean
方法是doCreateBean
的高层次包装器,负责创建并初始化一个新的 bean 实例。 -
lambda$doGetBean$0:337, AbstractBeanFactory (org.springframework.beans.factory.support)
这是一个 lambda 表达式,作为回调传递给getSingleton
方法,负责返回一个单例 bean 实例。 -
getObject:-1, AbstractBeanFactory$$Lambda$330/0x0000020b811ebab8 (org.springframework.beans.factory.support)
这是 lambda 表达式的实际实现,用于从单例注册表中获取或创建一个单例 bean。 -
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
getSingleton
方法从单例缓存中获取 bean 实例,如果 bean 不存在,则创建并缓存它。 -
doGetBean:335, AbstractBeanFactory (org.springframework.beans.factory.support)
doGetBean
方法负责实际获取一个 bean 实例,包括处理依赖注入和初始化。 -
getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean
方法是doGetBean
的高层次包装器,用于获取一个 bean 实例。 -
preInstantiateSingletons:975, DefaultListableBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons
方法负责提前实例化所有非延迟加载的单例 bean,确保它们在应用启动时就被创建和初始化。 -
finishBeanFactoryInitialization:962, AbstractApplicationContext (org.springframework.context.support)
finishBeanFactoryInitialization
方法在 Spring 容器刷新期间被调用,完成 bean 工厂的初始化,包括实例化所有剩余的单例 bean。 -
refresh:624, AbstractApplicationContext (org.springframework.context.support)
refresh
方法是 Spring 应用上下文刷新逻辑的入口点,负责重新加载 bean 定义、初始化 Spring 环境和 bean 工厂。 -
refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
这是ServletWebServerApplicationContext
类中refresh
方法的实现,用于刷新 Web 应用上下文。 -
refresh:754, SpringApplication (org.springframework.boot)
refresh
方法是 Spring Boot 应用启动过程中刷新应用上下文的关键步骤。 -
refreshContext:456, SpringApplication (org.springframework.boot)
refreshContext
方法用于刷新 Spring 应用上下文,确保所有 bean 定义和依赖关系都被正确加载和初始化。 -
run:335, SpringApplication (org.springframework.boot)
run
方法是 Spring Boot 应用的主要入口点,负责启动 Spring 应用上下文。 -
run:1363, SpringApplication (org.springframework.boot)
这是run
方法的一种签名,通常用于启动 Spring Boot 应用。 -
run:1352, SpringApplication (org.springframework.boot)
这是run
方法的一种签名,通常用于启动 Spring Boot 应用。 -
main:10, DemoApplication (com.xun.demo)
这是应用的主类DemoApplication
的main
方法,是 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 应用程序的入口类,下面是对每个部分的详细解释:
@SpringBootApplication
这个注解是一个组合注解,它结合了以下三个常用的 Spring 注解:
@SpringBootConfiguration
: 表示这是一个 Spring Boot 配置类,等同于 Spring 的@Configuration
注解。@EnableAutoConfiguration
: 这个注解告诉 Spring Boot 根据添加的依赖自动配置 Spring 应用程序。@ComponentScan
: 这个注解启用组件扫描,让 Spring 找到并注册带有@Component
、@Service
、@Repository
和@Controller
等注解的类。
-
public class DemoApplication
这是 Spring Boot 应用的主类,命名为DemoApplication
。按照惯例,Spring Boot 应用的主类通常位于源码目录的根包下,以便能够扫描到该包及其子包中的所有组件。 -
public static void main(String[] args)
这是 Java 应用程序的入口点。main
方法是应用程序启动时第一个执行的方法。 -
SpringApplication.run(DemoApplication.class, args)
这个静态方法启动了 Spring Boot 应用程序。以下是一些关键点:
SpringApplication.run
方法负责启动 Spring 应用上下文,启动内嵌的服务器(例如 Tomcat),并初始化 Spring 环境。DemoApplication.class
是应用的主类,它包含@SpringBootApplication
注解,使其成为 Spring Boot 应用的配置类。args
是传递给应用的命令行参数。
当 SpringApplication.run
被调用时,Spring Boot 会执行以下步骤:
- 创建并启动
SpringApplication
实例。 - 准备
SpringApplication
实例(例如,读取并解析命令行参数)。 - 创建并刷新应用上下文(包括创建所有单例 bean)。
- 启动嵌入式服务器(例如 Tomcat)。
- 执行所有
CommandLineRunner
和ApplicationRunner
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
方法的主要流程和作用:
-
创建启动记录实例: 创建一个
Startup
实例,用于记录应用启动的时间和事件。 -
注册 JVM 关闭钩子: 如果配置了
registerShutdownHook
,则注册一个 JVM 关闭钩子,以确保在 JVM 关闭时能正确地进行清理工作。 -
创建引导上下文: 创建一个
DefaultBootstrapContext
实例,用于在应用上下文初始化期间保存引导信息。 -
配置
headless
属性: 配置headless
属性,以防止在没有显示器环境中运行时的一些问题。 -
获取运行监听器: 获取
SpringApplicationRunListeners
实例,用于在不同的应用启动阶段触发事件。 -
通知监听器应用程序正在启动: 通知监听器应用程序启动的开始。
-
解析命令行参数: 创建
ApplicationArguments
实例,解析传入的命令行参数。 -
准备并配置环境: 准备并配置 Spring 的环境对象。
-
打印启动横幅: 根据环境配置打印应用启动横幅。
-
创建应用上下文: 创建
ApplicationContext
实例,用于管理 Spring beans。 -
设置应用启动器: 将应用启动器设置到上下文中。
-
准备应用上下文: 准备应用上下文,包括加载 bean 定义等。
-
刷新应用上下文: 刷新应用上下文,实际启动 Spring 容器。
-
执行刷新后操作: 在上下文刷新后执行一些必要的操作。
-
记录启动时间: 记录应用程序启动的时间。
-
记录启动信息: 如果启用了启动信息日志,则记录启动信息。
-
通知监听器应用程序已启动: 通知监听器应用程序已经启动完成。
-
调用命令行运行器: 调用所有实现
CommandLineRunner
和ApplicationRunner
接口的类。 -
处理启动失败: 捕获并处理启动过程中出现的异常。
-
通知监听器应用程序已就绪: 通知监听器应用程序已经准备就绪,可以处理请求。
-
返回应用上下文: 返回运行中的
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 应用上下文的整个流程。以下是其详细流程总结:
- 获取启动和关闭锁: 获取
startupShutdownLock
锁,确保在应用上下文刷新期间不会被其他线程中断。 - 设置当前线程为启动/关闭线程: 将当前线程设置为
startupShutdownThread
,以便在需要时能够进行相应处理。 - 应用启动步骤记录: 使用
applicationStartup
记录应用启动步骤,标记为 “spring.context.refresh”。用于记录上下文刷新过程的性能指标。 - 准备刷新操作: 调用
prepareRefresh()
方法,为刷新操作做准备。包括设置必要的环境属性和初始化一些状态。 - 获取新的 Bean 工厂: 调用
obtainFreshBeanFactory()
方法获取一个新的ConfigurableListableBeanFactory
实例。通常是通过重新加载Bean定义来实现的。 - 为使用准备 Bean 工厂: 调用
prepareBeanFactory(beanFactory)
方法,为当前上下文的使用准备 Bean 工厂。包括设置类加载器、注册默认的环境Bean等。 - 后处理 Bean 工厂: 调用
postProcessBeanFactory(beanFactory)
方法,允许子类对 Bean 工厂进行后处理。 - 调用 Bean 工厂后处理器: 调用
invokeBeanFactoryPostProcessors(beanFactory)
方法,执行注册在上下文中的 Bean 工厂后处理器。 - 注册 Bean 后处理器: 调用
registerBeanPostProcessors(beanFactory)
方法,注册拦截 Bean 创建的 Bean 后处理器。 - 初始化消息源: 调用
initMessageSource()
方法,初始化该上下文的消息源。 - 初始化应用事件广播器: 调用
initApplicationEventMulticaster()
方法,初始化该上下文的应用事件广播器。用于发布和监听应用事件。 - 特定上下文子类的初始化: 调用
onRefresh()
方法,初始化特定于该上下文子类的其他特殊 Bean。 - 注册监听器: 调用
registerListeners()
方法,检查并注册监听器 Bean。 - 完成 Bean 工厂的初始化: 调用
finishBeanFactoryInitialization(beanFactory)
方法,实例化所有剩余的非延迟初始化单例 Bean。 - 完成刷新操作: 调用
finishRefresh()
方法,完成应用上下文的刷新。 - 处理异常: 在执行上述步骤的任何过程中,如果捕获到
RuntimeException
或Error
,则进入异常处理块。
- 记录警告日志,取消刷新尝试。
- 销毁已创建的单例 Bean,避免资源泄漏。
- 设置
active
标志为 false。 - 向上层抛出捕获到的异常。
- 结束应用启动步骤记录: 无论是否发生异常,都会调用
contextRefresh.end()
结束应用启动步骤记录。 - 释放启动和关闭锁: 最后在
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在应用启动时已经准备就绪,从而提高应用的响应速度和稳定性。
以下是方法的流程总结:
-
日志记录:
- 如果日志级别为 trace,则记录日志,指示正在预实例化单例 Beans。
-
复制 Bean 名称列表:
- 创建一个
beanNames
列表,复制当前 Bean 工厂中所有的 Bean 名称。这样做是为了允许在初始化方法期间可能注册新的 Bean 定义。(可以安全地在初始化过程中注册新的bean定义)
- 创建一个
-
实例化所有非延迟初始化的单例 Beans:
- 遍历
beanNames
列表中的每个 Bean 名称。 - 获取与当前 Bean 名称关联的合并的 RootBeanDefinition。
- 检查该 Bean 定义是否为非抽象、单例且非延迟初始化。
- 如果是工厂Bean
FactoryBean
,则首先获取其对应的 FactoryBean 实例,如果是SmartFactoryBean
并且设置为 eagerInit(需要急切初始化),则立即获取其实例。 - 否则,直接通过
getBean(beanName)
方法获取该 Bean 的实例。
- 遍历
-
触发所有适用 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链接
相关文章:
Springboot 启动时Bean的创建与注入(一)-面试热点-springboot源码解读-xunznux
Springboot 启动时Bean的创建与注入,以及对应的源码解读 文章目录 Springboot 启动时Bean的创建与注入,以及对应的源码解读构建Web项目流程图:堆栈信息:堆栈信息简介堆栈信息源码详解1、main:10, DemoApplication (com.xun.demo)2…...
单调栈(随缘复习到了,顺手刷了)
也是不知道为什么突然又复习到单调栈了,所以顺手刷了三道题,总结一下 P6503 [COCI2010-2011#3] DIFERENCIJA 思路:这题是要求每个子区间里面的最大值和最小值的差,我们一开始想的必然是纯暴力呀,但是一看这数据&#…...
学习测试10-3自动化 web自动化
web自动化 chrome驱动下载地址: https://registry.npmmirror.com/binary.html?pathchromedriver/ https://googlechromelabs.github.io/chrome-for-testing/#stable观察Google版本,下相应的驱动 运行代码试试,成功Google就会弹出 from se…...
安防视频监控EasyCVR视频汇聚平台修改配置后无法启动的原因排查与解决
安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构,兼容性强、支持多协议接入,包括国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SD…...
爬虫学习2:爬虫爬取网页的信息与图片的方法
爬虫爬取网页的信息与图片的方法 爬取人物信息 import requestshead {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" } # 这是get请求带参数的模式…...
MySQL定时备份数据,并上传到oss
1.环境准备 1.安装阿里云的ossutil 2.安装mysql 2.编写脚本 脚本内容如下 #!/bin/bash # 数据库的配置信息,根据自己的情况进行填写 db_hostlocalhost db_usernameroot db_passwordroot db_namedb_root # oss 存贮数据的bucket地址 bucket_namerbsy-backup-buck…...
极速删除 node_modules 仅3 秒()
今天教大家如何快速删除 node_modules 依赖的一个小秘诀,告别繁琐!!! 前言 作为前端开发者,相信大家都曾经历过删除 node_modules 文件夹时的漫长等待。 尤其是在处理那些依赖库繁多的项目时,删除操作…...
vue this.$refs 动态拼接
业务需要,refs是不固定的 <vxe-grid refgridWarehouse v-bind"gridWarehouseOptions" v-if"tableHeight" :height"tableHeight":expand-config"{iconOpen: vxe-icon-square-minus, iconClose: vxe-icon-square-plus}"c…...
一次搞定!中级软件设计师备考通关秘籍
大家好,我是小欧! 今天我们来聊聊软考这个话题。要是你准备参加计算机技术与软件专业技术资格(软考),那么这篇文章就是为你量身定做的。话不多说,咱们直接进入正题。 什么是软考? 软考…...
第十六讲 python中的序列-列表简介-特点-常用方法-创建-添加-删除-访问-切片-排序-复制-反转
目录 1. 序列的本质和内存结构 2.列表 2.1 列表简介 2.2 列表的特点 2.3 列表对象的常用方法大全: 2.4 列表的创建 2.4.1 使用方括号 [] 2.4.2 使用 list() 函数 2.4.3 使用 range() 函数 2.4.3.1 range的基本用法 2.4.3.2 返回值 2.4.3.3 range的使用例子 2.4.3.4 range的使…...
大模型日报 2024-07-22
大模型日报 2024-07-22 大模型资讯 谷歌将在ICML 2024展示机器学习研究成果 摘要: 谷歌研究人员将在ICML 2024会议上展示他们在机器学习领域的探索,从理论到应用,构建解决深层问题的ML系统。 代理符号学习:优化AI系统符号组件的框架 摘要: 大…...
Electron 的open-file事件
在 Electron 中,open-file 事件是一个重要的事件,它允许开发者在应用程序已经运行的情况下,通过文件打开请求(如双击文件或在命令行中使用 open 命令打开文件)来捕获文件路径。以下是对 open-file 事件的详细解析: 触发条件 应用已经打开。用户通过双击与应用程序关联的…...
前端面试 vue 接口权限控制
接口权限目前一般采用jwt的形式来验证,没有通过的话一般返回401,跳转到登录页面重新进行登录 对于 jwt的理解 (前端接口权限的控制主要通过接口权限配置和JWT(Json Web Token)技术来实现。 首先,…...
【DevOps系列】构建Devops系统
开始介绍 那就着手开始干吧。先介绍一下我们的工具链。 主要工具:GitHub、Jenkins、Kubernetes、Ansible、Prometheus和JMeter 着手动 1. 设置GitHub作为源代码仓库 登录GitHub: 打开浏览器并访问 https://github.com,使用您的GitHub账户登录。 创建…...
ABAP打印WORD的解决方案
客户要求按照固定格式输出到WORD模板中,目前OLE和DOI研究了均不太适合用于这种需求。 cl_docx_document类可以将WORD转化为XML文件,利用替换字符串方法将文档内容进行填充同 时不破坏WORD现有格式。 首先需要将WORD的单元格用各种预定义的字符进行填充,为后续替换作准备…...
emr部署hive并适配达梦数据库
作者:振鹭 一、达梦 用户、数据库初始化 1、创建hive的元数据库 create tablespace hive_meta datafile /dm8/data/DAMENG/hive_meta.dbf size 100 autoextend on next 1 maxsize 2048;2、创建数据库的用户 create user hive identified by "hive12345&quo…...
王春城:怎么用精益思维重塑企业战略规划格局?
当下,企业战略规划的灵活性和适应性变得至关重要。传统的战略规划方法往往过于僵化和静态,难以应对市场的不确定性和变化。因此,引入精益思维来重塑企业战略规划格局,成为了许多企业寻求突破和创新的途径。具体步骤如深圳天行健企…...
git reset
git reset [--soft | --mixed | --hard] [HEAD] 表格版 原始内容reset前reset命令reset后本地工作区暂存区本地仓库本地工作区暂存区本地仓库本地工作区暂存区本地仓库READMEREADMEREADMEREADMEREADMEREADME--soft HEADREADMEREADMEREADMEa.txta.txtb.txtb.txtb.txtb.txtc.tx…...
E17.【C语言】练习:sizeof和strlen的辨析
先回顾http://t.csdnimg.cn/aYHl6 1. char acX[] "abcdefg"; char acY[] { a,b,c,d,e,f,g}; 以下说法正确的是( ) A.数组acX和数组acY等价 B.数组acX和数组acY的长度相同 C.sizeof(acX)>sizeof (acY) D.strlen (acX)>strlen (acY) 分析:…...
便携气象站:科技助力气象观测
在科技飞速发展的今天,便携气象站以其轻便、高效、全面的特点,正逐渐改变着气象观测的传统模式。这款小巧而强大的设备,不仅为气象学研究和气象灾害预警提供了有力支持,更为户外活动、农业生产等领域带来了诸多便利。 便携气象站是…...
php 存储复杂的json格式查询(如:经纬度)
在开发中,有时我们可能存了一些复杂json格式不知道怎么查。我这里提供给大家参考下: 一、先上表数据格式(location字段的possiton经纬度以逗号分开的) {"title":"澳海文澜府","position":"11…...
UDP网口(1)概述
文章目录 1.计算机网络知识在互联网中的应用2.认识FPGA实现UDP网口通信3.FPGA实现UDP网口通信的方案4.FPGA实现UDP网口文章安排5.传送门 1.计算机网络知识在互联网中的应用 以在浏览器中输入淘宝网为例,介绍数据在互联网是如何传输的。我们将要发送的数据包称作A&a…...
Linux - 进程的概念、状态、僵尸进程、孤儿进程及进程优先级
进程基本概念 课本概念:在编程或软件工程的上下文中,进程通常被视为正在执行的程序的实例。当你启动一个应用程序时,操作系统会为这个程序创建一个进程。每个进程都有自己的独立内存空间,可以运行自己的指令序列,并可能…...
Gradle依赖报告:项目依赖树的X光机
Gradle依赖报告:项目依赖树的X光机 在复杂的软件项目中,依赖管理是确保应用正常构建和运行的关键。Gradle作为一个强大的构建工具,提供了依赖报告功能,帮助开发者分析和理解项目的依赖树。本文将详细介绍如何在Gradle中使用依赖报…...
开源XDR-SIEM一体化平台 Wazuh (1)基础架构
简介 Wazuh平台提供了XDR和SIEM功能,保护云、容器和服务器工作负载。这些功能包括日志数据分析、入侵和恶意软件检测、文件完整性监控、配置评估、漏洞检测以及对法规遵从性的支持。详细信息可以参考Wazuh - Open Source XDR. Open Source SIEM.官方网站 Wazuh解决…...
从零开始:构建基于深度学习的实时跌倒检测系统(UI界面+YOLO代码+数据集)
注意看文末的结局与声明 一、引言 1. 项目背景与动机 在老年人和高危职业环境中,跌倒是一种常见的事故,可能导致严重的伤害甚至致命。实时跌倒检测系统可以及时发现并报警,提供紧急救助。通过深度学习技术,可以提高跌倒检测的准…...
【策略模式在项目中的实际应用】
业务场景 最最近项目中有这样的一个业务场景: 用户下单->管理员审核->配送员接单->配送中->送达–>签收->完成 整个业务以这种流程的形式存在,每个流程状态的业务不一样,考虑到多种状态如果直接写一个接口肯定会嵌套太多…...
昇思25天学习打卡营第14天|计算机视觉
昇思25天学习打卡营第14天 文章目录 昇思25天学习打卡营第14天FCN图像语义分割语义分割模型简介网络特点数据处理数据预处理数据加载训练集可视化 网络构建网络流程 训练准备导入VGG-16部分预训练权重损失函数自定义评价指标 Metrics 模型训练模型评估模型推理总结引用 打卡记录…...
将json数组格式转成数组
start cmd [ 27 01 f7 01 24 38 02 b7 42 6e ee 2f 69 46 72 21 74 44 c4 22 7a 92 d8 6a de 66 61 b1 1e 2f de ee 5c 31 57 db df 01 31 2d c9 01 01 c0 FB ] set_ulpk {“jsonrpc”:“2.0”,“type”:2,“id”:0,“method”:“method”,“message”:{“VALUE”:[56,2,183,66…...
接口测试之测试原则、测试用例、测试流程详解
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 一、接口的介绍 软件测试中,常说的接口有两种:图形用户接口(GUI,人与程序的接口)、应用程序编程接口&…...
公司可以做网站吗/亚马逊seo推广
页面效果 场景: 比如使用layui.table数据表格显示了所有项目数据,客户数据对应另外的表数据集合,现在需要修改客户名称,需要把数据回显到表单中,然后进行修改 实现 <div class"layui-form-item"><label class…...
网站开发论文创作背景/b2b采购平台
1. 问题描述: 我们称一个序列是有序的,如果该序列是非严格单调递增序列或非严格单调递减序列。现在,给定一个 n 个整数的序列 a1,a2,…,an。请你找到该序列的最短非有序子序列。注意,子序列不一…...
哪里学做网站/排名优化工具
XHTML 与 HTML 4.01 标准没有太多的不同。 另外,你应该马上使用小写字母编写HTML代码,同时绝不要养成忽略类似 </p> 标签的坏习惯。 最主要的不同:XHTML 元素必须被正确地嵌套。XHTML 元素必须被关闭。标签名必须用小写字母。XHTML 文档…...
网站建设 小程序开发/福州seo外包公司
年会是遇到抽奖环节如何制作ppt呢,这里有个解决方法,提供参考。步骤就是这样:1、新建ppt.2、添加一个命令控件和一个标签控件(开发工具---控件---命令控件&标签控件)3、点击“命令按钮CommandButton1”右击鼠标&am…...
外贸获客软件排名前十名/甘肃省seo关键词优化
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 你的算法时间复杂度必须是 O(log n) 级别。 如果数组中不存在目标值,返回 [-1, -1]。 示例 1: 输入: nums [5,7,7,8,8,10], target 8 输…...
全屋定制报价明细表/暴疯团队seo课程
List:对付顺序的好帮手List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象Set:注重独一无二的性质不允许重复的集合。不会有多个元素引用相同的对象。Map:用Key来搜索的专家使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相…...