首页 > 其他分享 >SpringBoot 启动流程追踪(第二篇)

SpringBoot 启动流程追踪(第二篇)

时间:2023-08-14 22:14:50浏览次数:37  
标签:初始化 SpringBoot beanFactory 方法 应用程序 Bean postProcessor 第二篇 追踪

上一篇文章分析了除 refresh 方法外的流程,并着重分析了 load 方法,这篇文章就主要分析 refresh 方法,可以说 refresh 方法是 springboot 启动流程最重要的一环,没有之一。

try {
	// Allows post-processing of the bean factory in context subclasses.
	postProcessBeanFactory(beanFactory);
	StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
	// Invoke factory processors registered as beans in the context.
	invokeBeanFactoryPostProcessors(beanFactory);
	// Register bean processors that intercept bean creation.
	registerBeanPostProcessors(beanFactory);
	beanPostProcess.end();
	// Initialize message source for this context.
	initMessageSource();
	// Initialize event multicaster for this context.
	initApplicationEventMulticaster();
	// Initialize other special beans in specific context subclasses.
	onRefresh();
	// Check for listener beans and register them.
	registerListeners();
	// Instantiate all remaining (non-lazy-init) singletons.
	finishBeanFactoryInitialization(beanFactory);
	// Last step: publish corresponding event.
	finishRefresh();
}

该方法是由 AnnotationConfigServletWebServerApplicationContext 类调用的,不过它也是调用的父类的方法,所以你可以直接挪到 AbstractApplicationContext 抽象类即可。

一、invokeBeanFactoryPostProcessors

if (beanFactory instanceof BeanDefinitionRegistry) {
	BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
	List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
	List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
	for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
		if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
			BeanDefinitionRegistryPostProcessor registryProcessor =
					(BeanDefinitionRegistryPostProcessor) postProcessor;
			registryProcessor.postProcessBeanDefinitionRegistry(registry);
			registryProcessors.add(registryProcessor);
		}
		else {
			regularPostProcessors.add(postProcessor);
		}
	}
	......
}

这里的 beanFactory 是 DefaultListableBeanFactory,它是 BeanDefinitionRegistry 是实例,至于怎么来的我想你应该清楚哈。然后在这片段代码里,我们比较注意的是 registryProcessor.postProcessBeanDefinitionRegistry(registry)。
这时你应该想到 beanFactoryPostProcessors 的值是怎么来的,如果你往前追溯的话,可以知道是在 prepareContext 方法里赋值的。这里我们选其中一个 SharedMetadataReaderFactoryContextInitializer 看看:
它会注册 SharedMetadataReaderFactoryBean 工厂并且会额外注册一个 processor,这也是后面为啥会多次遍历的原因。
另外需要注意到的是它分成了两个类型的 processor,一个是 regularPostProcessors,另一个是 registryProcessors。

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
		beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
	if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
		currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
		processedBeans.add(ppName);
	}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();

接着我们看这段代码,这里的注释意思很清楚:不要在这里实例化 FactoryBeans,我们需要把所有的 regular beans 留给 beanFactory 的 post-processors 去处理。然后将 BeanDefinitionRegistryPostProcessors 分离为实现了 PriorityOrdered、Ordered 两类,以及剩下的为一类,然后调用 invokeBeanDefinitionRegistryPostProcessors 方法进行处理。

private static void invokeBeanDefinitionRegistryPostProcessors(
		Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
	for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
		StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
				.tag("postProcessor", postProcessor::toString);
		postProcessor.postProcessBeanDefinitionRegistry(registry);
		postProcessBeanDefRegistry.end();
	}

我们以其中一个 postProcessor 进行说明,比如 ConfigurationClassPostProcessor:
它的作用是在应用程序上下文启动时,扫描配置类(被 @Configuration 注解标记的类)并解析其中的 @Bean 注解,将其转化为对应的BeanDefinition对象,并注册到BeanDefinitionRegistry 中。这样,这些配置类中的 @Bean 方法就可以被容器识别并实例化为相应的 Bean 对象。
接着就开始调用 invokeBeanFactoryPostProcessors 来处理 regularPostProcessors 和 registryProcessors,

private static void invokeBeanFactoryPostProcessors(
		Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
	for (BeanFactoryPostProcessor postProcessor : postProcessors) {
		StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
				.tag("postProcessor", postProcessor::toString);
		postProcessor.postProcessBeanFactory(beanFactory);
		postProcessBeanFactory.end();
	}
}

它会调用这些工厂的 postProcessBeanFactory 方法,还是拿 ConfigurationClassPostProcessor 举例:
它的作用是对 BeanFactory 进行进一步的处理和配置,确保配置类中的 Bean 能够正确注册到容器中,并处理配置类之间的依赖关系和导入关系。

二、registerBeanPostProcessors

该方法和上面的步骤也差不多,registerBeanPostProcessors 方法的作用是将所有的 BeanPostProcessor(后置处理器)注册到 BeanFactory 中。在应用程序上下文启动时,会调用该方法来注册所有的 BeanPostProcessor,以便在 Bean 的创建过程中进行相应的处理。BeanPostProcessor 是 Spring 框架提供的一种机制,用于在Bean的实例化和初始化过程中对 Bean 进行额外的处理。它可以在 Bean 实例化之前和初始化之后对 Bean 进行修改、增强或进行其他操作。通常,开发人员可以通过实现 BeanPostProcessor 接口来自定义自己的后置处理器。registerBeanPostProcessors 方法的主要作用是将所有实现了 BeanPostProcessor 接口的类实例化,并添加到 BeanFactory 中的后置处理器列表中。这样,在 Bean 的创建过程中,Spring 容器会依次调用这些后置处理器的方法,对 Bean 进行相应的处理。这些处理器可以对 Bean 进行增强、修改属性值、解析注解等操作,以满足特定的需求。

三、onRefresh

该方法会创建 WebServer:

private void createWebServer() {
	WebServer webServer = this.webServer;
	ServletContext servletContext = getServletContext();
	if (webServer == null && servletContext == null) {
		StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
		ServletWebServerFactory factory = getWebServerFactory();
		createWebServer.tag("factory", factory.getClass().toString());
		this.webServer = factory.getWebServer(getSelfInitializer());
		createWebServer.end();
		getBeanFactory().registerSingleton("webServerGracefulShutdown",
				new WebServerGracefulShutdownLifecycle(this.webServer));
		getBeanFactory().registerSingleton("webServerStartStop",
				new WebServerStartStopLifecycle(this, this.webServer));
	}
	else if (servletContext != null) {
		try {
			getSelfInitializer().onStartup(servletContext);
		}
		catch (ServletException ex) {
			throw new ApplicationContextException("Cannot initialize servlet context", ex);
		}
	}
	initPropertySources();
}

四、finishBeanFactoryInitialization

finishBeanFactoryInitialization 法的作用是完成 BeanFactory 的初始化过程。在应用程序上下文启动时,会调用该方法来完成 BeanFactory 的初始化工作,包括实例化和初始化所有的非延迟加载的单例 Bean。
具体来说,finishBeanFactoryInitialization 方法会按照以下步骤完成 BeanFactory 的初始化:

  1. 实例化所有非延迟加载的单例 Bean:根据 Bean 定义信息,通过反射或其他方式实例化所有非延迟加载的单例 Bean,并将它们放入容器中。
  2. 依赖注入:对所有实例化的 Bean 进行依赖注入,即将它们所依赖的其他 Bean 注入到相应的属性中。
  3. 初始化:对所有实例化并注入依赖的 Bean 进行初始化。这包括调用 Bean 的初始化方法(如果有定义的话),以及应用任何配置的 Bean 后置处理器对 Bean 进行处理。
  4. 完成 BeanFactory 的初始化:将 BeanFactory 的状态设置为已初始化完成,标记整个初始化过程的结束。

通过调用 finishBeanFactoryInitialization 方法,Spring 容器能够确保所有非延迟加载的单例 Bean 都被正确实例化、注入依赖和初始化,从而使它们可以在应用程序中被正常使用。

五、finishRefresh

finishRefresh 方法的作用是在应用程序上下文刷新完成后执行一些额外的操作。
在应用程序上下文的刷新过程中,会调用该方法来完成一些与刷新相关的收尾工作。
具体来说,finishRefresh方法会按照以下步骤完成额外的操作:

  1. 初始化生命周期处理器:对于实现了 Lifecycle 接口的 Bean,会调用它们的 start 方法来启动这些 Bean。
  2. 发布应用程序上下文已完成事件:通过 ApplicationEventPublisher,发布一个 ContextRefreshedEvent 事件,通知其他监听器应用程序上下文已完成刷新。
  3. 注册 ApplicationListener 的 Bean:将实现了 ApplicationListener 接口的 Bean 注册到应用程序上下文中,以便监听其他事件。
  4. 初始化其他单例 Bean:对于非延迟加载的单例 Bean,会调用它们的初始化方法(如果有定义的话)。
  5. 发布应用程序上下文已启动事件:通过 ApplicationEventPublisher,发布一个 ContextStartedEvent 事件,通知其他监听器应用程序上下文已启动。

通过调用finishRefresh方法,Spring容器能够在应用程序上下文刷新完成后执行一些额外的操作,如启动生命周期处理器、发布事件等。这些操作可以用于执行一些初始化、通知或其他自定义的逻辑,以满足特定的需求。

6、总结

refresh 方法感觉还是比较难以分析,后面部分文字内容还是借鉴了 ChatGPT,感觉如果你想知道某个函数的作用时,直接问它,它或许会告诉你正确答案,比如你这样问:finishRefresh 方法的作用是啥?

标签:初始化,SpringBoot,beanFactory,方法,应用程序,Bean,postProcessor,第二篇,追踪
From: https://www.cnblogs.com/M-Anonymous/p/17618207.html

相关文章

  • 还在手动更改SpringBoot的环境yml配置文件?老鸟带你可视化配置
    问题说明:在SpringBoot开发时、SpringBoot的特性:‘约定大于配置’,我们只需要在**application.yml**配置当前的环境变量属与那个文件比如测试环境‘application-test.yml’,我们需要手动指定application.yml中配置profiles:active:test我们总会设置一些配置文件我们需要手动......
  • Springboot中实现适配器模式
    当在SpringBoot中实现适配器模式时,可以按照以下步骤进行详细的实现:1.首先,定义一个目标接口(TargetInterface):publicinterfaceTarget{voidrequest();}目标接口定义了适配器需要实现的方法。2.创建一个适配器类(AdapterClass),实现目标接口,并适配一个已有的类或接......
  • SpringBoot——整合WebSocket(基于STOMP协议)
    参考链接Spring官文:https://docs.spring.io/spring-framework/docs/6.0.0-SNAPSHOT/reference/html/web.html#websocket-stomp-benefits前端页面:https://github.com/callicoder/spring-boot-websocket-chat-demoSTOMP定义STOMP中文为“面向消息的简单文本协议”,STOM......
  • springboot整合nacos和dubbo
    0.源码源码:gitee1.版本java:1.8.0_281nacos:2.1.22.创建项目创建一个简单的springboot或者maven项目,或者代码库(gitee/github/其他代码库管理平台)创建一个空白的拉下来,最后只保留一个pom.xml即可.2.1根项目依赖版本控制参考:版本说明其中有一句话Spring......
  • SpringBoot有几种获取Request对象的方法?
    HttpServletRequest简称Request,它是一个ServletAPI提供的对象,用于获取客户端发起的HTTP请求信息。例如:获取请求参数、获取请求头、获取Session会话信息、获取请求的IP地址等信息。那么问题来了,在SpringBoot中,获取Request对象的方法有哪些?常见的获取Request对......
  • springboot开启prometheus可采集的指标配置
    1、引包<!--实现对Actuator的自动化配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>......
  • SpringBoot实现大文件上传
    ​ 对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率。 本文是基于springboot+vue实现的文件上传,本文主要介绍服务端实现文件......
  • 使用Spring initializr快速创建一个springboot项目
     第一步首先new一个新的project选择Springinitializr配置好相关信息后下一步编辑在左上角我们可以选择SprinBoot的版本,在这里直接加入web依赖springweb和Template的Thymeleaf依赖,点上对勾后可以在最右边里看到你选择的依赖,然后点击create编辑可以看到我们的项目结构,很多sprin......
  • 手摸手3-springboot整合swagger-ui,实现自动文档
    (目录)手摸手3-springboot整合swagger-ui,实现自动文档修改pom.xml<!--解决FluentIterable.class找不到问题--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>26.0-jre</version>......
  • springboot中tomcat线程池
    一、Tomcat中的默认配置线程任务就是一个连接的请求,每个请求都会尝试创建线程来处理。最大工作线程数,默认200。server.tomcat.max-threads=200最大连接数默认是10000,同时支持的并发连接数server.tomcat.max-connections=10000等待队列长度,默认100。server.tomcat.acce......