零维护

 找回密码
 立即注册
快捷导航
搜索
热搜: 活动 交友 discuz
查看: 89|回复: 2

SpringBoot源码 | refreshContext方法解析

[复制链接]

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2022-9-21 14:53:28 | 显示全部楼层 |阅读模式
简介:本文主要讲述SpringBoot启动流程源码中的refreshContext()方法
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. 初始化特定上下文子类中的其他特殊bean
onRefresh();
// 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#setRequiredProperties
getEnvironment().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核心中的公共内省缓存,整体流程比较清晰,源码给的注释也很丰富,很方便对源码的学习。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
回复

使用道具 举报

1

主题

4

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2025-3-11 06:16:18 | 显示全部楼层
看起来好像不错的样子
回复

使用道具 举报

1

主题

5

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2025-3-27 18:47:07 | 显示全部楼层
very good
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver| 手机版| 小黑屋| 零维护

GMT+8, 2025-4-8 08:12 , Processed in 0.086651 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2020, LianLian.

快速回复 返回顶部 返回列表