SpringBoot源码 | refreshContext方法解析

news/2024/11/24 11:34:16/

SpringBoot源码 | refreshContext方法解析

  • SpringBoot
    • refreshContext源码
    • refresh方法
    • prepareRefresh
    • obtainFreshBeanFactory
    • prepareBeanFactory
    • postProcessBeanFactory
    • invokeBeanFactoryPostProcessors
    • registerBeanPostProcessors
    • initMessageSource
    • initApplicationEventMulticaster
    • onRefresh
    • registerListeners
    • finishBeanFactoryInitialization
    • finishRefresh
    • resetCommonCaches
  • 总结

SpringBoot

在SpringBoot启动流程中,主要的两个阶段是初始化SpringApplication对象以及SpringApplication.run方法执行的内容,今天主要细讲的是SpringApplication.run中的刷新容器refreshContext方法,refreshContext的源码如下

refreshContext源码

refreshContext方法主要是刷新容器,下面我们来看一下refreshContext的源码,点击SpringApplication.run方法的refreshContext方法

看到refreshContext的方法内容

继续点击refresh方法可以看到,Refresh the underlying {@link ApplicationContext}也就是刷新底层的ApplicationContext

继续跟进去,这里要选择AbstractApplicationContext

这里我们看一下AbstractApplicationContext的注释,注释内容
Abstract implementation of the {@link org.springframework.context.ApplicationContext} interface. Doesn’t mandate the type of storage used for configuration; simply implements common context functionality. Uses the Template Method design pattern,requiring concrete subclasses to implement abstract methods.翻译过来就是当前抽象类是ApplicationContext接口的抽象实现,不强制要求用于配置的存储类型;它只是实现了公共上下文功能,使用的是模板方法的设计模式,需要具体的子类来实现抽象方法。下面我们再看refresh方法

refresh方法

refresh方法主要是刷新应用程序上下文,这里主要涉及到准备刷新上下文,调用上下文注册为bean的工厂处理器,初始化上下文的消息源,初始化特定上下文子类中的其他特殊bean,检查监听器bean并注册,最后发布相应的事件并销毁已经创建的单例及重置active标志,整体的注解我都直接加在源码中了

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// Prepare this context for refreshing. 准备刷新上下文prepareRefresh();// Tell the subclass to refresh the internal bean factory. 告诉子类刷新内部bean工厂ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context. 准备bean工厂以用于此上下文prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses. 允许在上下文子类中对bean工厂进行后置处理postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context. 调用上下文中注册为bean的工厂处理器invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.  注册拦截器bean创建的bean处理器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. 初始化特定上下文子类中的其他特殊beanonRefresh();// Check for listener beans and register them. 检查监听器bean并注册registerListeners();// Instantiate all remaining (non-lazy-init) singletons. 实例化所有剩余的(非懒惰初始化)单例finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event. 最后一步:发布相应的事件finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources. 销毁已创建的单例以避免悬空资源destroyBeans();// Reset 'active' flag. 重置active标志cancelRefresh(ex);// Propagate exception to caller. 将异常传播到调用方throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...//重置Spring核心中的公共内省缓存,因为我们可能不再需要单例bean的元数据resetCommonCaches();contextRefresh.end();}}
}

下面我们继续看refresh方法内部子方法的源码

prepareRefresh

prepareRefresh方法主要是准备上下文以进行刷新、设置其启动日期和活动标志以及执行属性源的任何初始化,源码注释如下

/*** Prepare this context for refreshing, setting its startup date and* active flag as well as performing any initialization of property sources.*/
protected void prepareRefresh() {// Switch to active. 切换到激活this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);if (logger.isDebugEnabled()) {if (logger.isTraceEnabled()) {logger.trace("Refreshing " + this);}else {logger.debug("Refreshing " + getDisplayName());}}// Initialize any placeholder property sources in the context environment. 初始化上下文环境中的任何占位符属性源initPropertySources();// Validate that all properties marked as required are resolvable:验证标记为需要的所有属性是否可解析// see ConfigurablePropertyResolver#setRequiredProperties 主要看ConfigurablePropertyResolver#setRequiredPropertiesgetEnvironment().validateRequiredProperties();// Store pre-refresh ApplicationListeners... 预刷新应用监听器if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);}else {// Reset local application listeners to pre-refresh state. 重置应用监听器为预刷新状态this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Allow for the collection of early ApplicationEvents, 允许收集早期的应用事件在multicaster可用后一次性发布// to be published once the multicaster is available...this.earlyApplicationEvents = new LinkedHashSet<>();
}

继续返回跟进refresh方法,在prepareRefresh之后是通知子类刷新内部bean工厂obtainFreshBeanFactory

obtainFreshBeanFactory

obtainFreshBeanFactory方法内部一共有两个方法refreshBeanFactory和getBeanFactory,

其中refreshBeanFactory什么也不做,主要是拥有一个内部bean工厂,并且信任调用者通过公共方法注册bean或者BeanFactory’s,源码需要去看类GenericApplicationContext的refreshBeanFactory方法

下面我们再说getBeanFactory方法,获取bean工厂,源码注释是返回一个当前上下文的内部bean工厂

继续向下看refresh方法,是prepareBeanFactory方法

prepareBeanFactory

prepareBeanFactory方法是配置工厂标准的上下文特征,比如上下文类加载器、后置处理器

继续执行refresh方法,是postProcessBeanFactory方法

postProcessBeanFactory

postProcessBeanFactory方法这里由于我们启动时的WebApplicationType是SERVLET

所以这里我们选择如图,跟进去可以看到

为bean工厂添加后置处理器以及registerWebApplicationScopes为bean工厂注册特定的web作用域,之后继续执行

完成之后执行refresh方法内部的invokeBeanFactoryPostProcessors方法

invokeBeanFactoryPostProcessors

invokeBeanFactoryPostProcessors方法主要负责调用bean工厂的后置处理器

继续执行是registerBeanPostProcessors方法,

registerBeanPostProcessors

registerBeanPostProcessors方法主要是Instantiate and register all BeanPostProcessor beans respecting explicit order if given实例化并注册所有后置处理器bean,如果给定,则遵循显式顺序

继续执行

initMessageSource

initMessageSource方法主要是初始化应用上下文消息源,这个首先判断工厂类是否有beanName是messageSource的bean

有的通过bean工厂获取消息源

没有的话new一个DelegatingMessageSource对象

initApplicationEventMulticaster

initApplicationEventMulticaster方法主要是为上下文初始化事件多播,通过bean工厂获取beanName是applicationEventMulticaster的对象

如果没有的话就new一个SimpleApplicationEventMulticaster对象放回bean工厂

以应用上下文事件多播的beanName放入工厂

onRefresh

onRefresh方法执行刷新,

点进去可以看到

执行父类的onRefresh方法,创建web服务,createWebServer方法主要是获取应用上下文创建web服务初始化PropertySources

registerListeners

registerListeners方法是检查并注册监听器,同时不影响其他监听器

执行完成之后查看getApplicationEventMulticaster方法非null

finishBeanFactoryInitialization

finishBeanFactoryInitialization方法是实例化所有剩余的单例

继续执行看到finishRefresh方法,也是最后一步

finishRefresh

finishRefresh方法主要是发布事件,包括清除上下文资源缓存,为上下文初始化生命周期处理器,发布最终事件

执行完成最后一步之后到resetCommonCaches方法

resetCommonCaches

resetCommonCaches方法时重置Spring核心中的公共内省缓存,重置完成之后执行contextRefresh.end容器刷新结束方法,

执行完成之后打印日志

启动结束,至此,SpringBoot启动流程中的refreshContext方法功能执行完成

总结

在SpringBoot启动流程中,refreshContext虽然执行步骤较多,加载的类也比较丰富,从准备刷新上下文到为上下文准备bean工厂及配置上下文类加载器,后置处理器到初始化上下文消息源、事件多播以及最后的检查监听器并注册以及实例化剩余的单例bean,最后发布事件,重置Spring核心中的公共内省缓存,整体流程比较清晰,源码给的注释也很丰富,很方便对源码的学习。


http://www.ppmy.cn/news/37895.html

相关文章

730. 机器人跳跃问题(基础二分)

机器人正在玩一个古老的基于 DOS 的游戏。 游戏中有 N1N1 座建筑——从 00 到 NN 编号&#xff0c;从左到右排列。 编号为 00 的建筑高度为 00 个单位&#xff0c;编号为 ii 的建筑高度为 H(i)H(i) 个单位。 起初&#xff0c;机器人在编号为 00 的建筑处。 每一步&#xff…

如何防止softmax函数overflow和underflow?

上溢出&#xff1a;c极其大的时候&#xff0c;计算ece^cec下溢出&#xff1a;当c趋于负无穷的时候&#xff0c;分母是一个极小的数&#xff0c;导致下溢出 解决方法 令Mmax⁡xi,i1,2,⋯,nM\max{x_i}, i1,2,\cdots,nMmaxxi​,i1,2,⋯,n, 也就是所有xix_ixi​中的最大值&#xff…

android 应用性能监控软件,App性能监控工具,卡顿

(609条消息) android 应用性能监控软件,App性能监控工具_weixin_39940154的博客-CSDN博客 APP性能监测的各种工具 - ClareBaby01 - 博客园 (cnblogs.com) (609条消息) Android App性能监控工具_sdk 性能监测工具_一代小强的博客-CSDN博客 android studio自带Memory Monitor …

Java_Spring:10. 基于注解的 AOP 配置

目录 1 环境搭建 1.1 第一步&#xff1a;准备必要的代码和 jar 包 1.2 第二步&#xff1a;在配置文件中导入 context 的名称空间 1.3 第三步&#xff1a;把资源使用注解配置 1.4 第四步&#xff1a;在配置文件中指定 spring 要扫描的包 2 配置步骤 2.1 第一步&#xff1a…

iOS 文本二维码识别

在 WWDC 2022&#xff0c;苹果发布了VisionKit 中的 DataScannerViewController&#xff0c;这是一个可以在本地无网络状态下识别文本以及条形码的视图控制器&#xff0c;它的相应速度以及精度都是比较高的&#xff0c;他可以支持汉语&#xff08;简繁版均支持&#xff09;、英…

城市新型智慧能源体系建设的初步解决方案

为加快推进国家“双碳”战略和新型能源体系建设&#xff0c;努力实现负荷控制和用户精细化管理&#xff0c;按照“政府主导、电网组织、政企协同、用户实施”的指导原则&#xff0c;多地成立市/县级电力负荷管理中心&#xff0c;包括浙江宁波、慈溪、辽宁大连、湖南株洲、娄底、…

JDBC---查询所有学生信息的页面运行后报错:

原代码&#xff1a; <% page import"java.sql.Connection" %> <% page import"java.sql.DriverManager" %> <% page import"java.sql.PreparedStatement" %> <% page import"java.sql.ResultSet" %><%--C…

Python 自动化指南(繁琐工作自动化)第二版:零、前言

关于作者 Al Sweigart 是一名软件开发人员和技术书籍作者。Python 是他最喜欢的编程语言&#xff0c;他是该语言的几个开源模块的开发者。他的其他书籍可以在他的网站上根据知识共享许可免费获得。他的猫现在重 11 磅。 关于技术评审 Philip James 从事 Python 工作已经超过…