当前位置: 首页 > article >正文

【细读Spring Boot源码】重中之重refresh()

前言

版本:spring-boot-2.7.3 | spring-context-5.3.22

在Spring Boot启动过程中【细读Spring Boot源码】启动步骤 主流程详情7中applicationContext.refresh();这个操作是加载或刷新容器,把所有的配置转换成响应的对象并存入容器。
下面看下他的具体执行流程

调用主流程

主流程使用了模板模式是一个模板方法

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 步骤记录器记录开始
		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

		// Prepare this context for refreshing.
		// 准备刷新。详看1	
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		// 告诉子类刷新内部 bean 工厂,得到Bean工厂。详看2
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		//  准备 bean 工厂以在此上下文中使用。详看3
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			// 在标准初始化后修改应用程序上下文的内部bean工厂。
			// 所有bean定义都已加载,但尚未实例化bean。
			// 这允许在某些应用程序上下文实现中注册特殊的BeanPostProcessors等。
			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();
		}

		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.
			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...
			resetCommonCaches();
			contextRefresh.end();
		}
	}
}

详情1——准备刷新prepareRefresh()

Springboot容器没有直接使用AbstractApplicationContext抽象类默认实现方法,Springboot容器是AnnotationConfigServletWebServerApplicationContext类,实现如下

protected void prepareRefresh() {
	// 清除缓存,理解这里在清除之前容器的缓存。例如:nacos引导容器启动时使用的缓存
	this.scanner.clearCache();
	// 再调用AbstractApplicationContext抽象类默认实现
	super.prepareRefresh();
}

看下AbstractApplicationContext抽象类默认实现。

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
	// 验证标记为必需的所有属性都是可解析的
	getEnvironment().validateRequiredProperties();

	// Store pre-refresh ApplicationListeners...
	// 存储预刷新 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,
	// to be published once the multicaster is available...
	// 允许早期应用程序事件的集合,一旦multicaster可用就发布
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

详看2——obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	// 刷新Bean工厂
	refreshBeanFactory();
	// 得到Bean工厂
	return getBeanFactory();
}

其中refreshBeanFactory()是AbstractApplicationContext抽象类的一个抽象方法。
子类必须实现此方法才能执行实际的配置负载。在任何其他初始化工作之前,该方法都会通过refresh() 调用。
子类将创建一个新的bean工厂并持有对它的引用,或者返回它持有的单个BeanFactory实例。在后一种情况下,如果多次刷新上下文,通常会抛出IllegalStateException。
实际上就是:刷新内部 bean 工厂,把当前上下文的Bean工厂切换为当前的Bean工厂。

protected final void refreshBeanFactory() throws IllegalStateException {
	// 如果当前值 = 期望值,则原子地将值设置为给定的更新值
	// 这个来判断当前容器是否刷新过。即refreshed的值为false,就把值替换为true,并返回true。取反
	if (!this.refreshed.compareAndSet(false, true)) {
		// 如果刷新过就抛出异常
		throw new IllegalStateException(
				"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
	}
	// 替换。为序列化目的指定一个id,如果需要的话,允许这个BeanFactory从这个id反序列化回BeanFactory对象。
	this.beanFactory.setSerializationId(getId());
}

详看3——prepareBeanFactory(beanFactory)

配置这个工厂的标准上下文特征,例如上下文的类加载器和后处理器。

void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);这个方法:添加一个新的BeanPostProcessor,该处理器将应用于该工厂创建的bean。在工厂配置期间调用。

注意: 此处提交的后处理器将按注册顺序应用; 通过实现org.springframework.core.Ordered接口表达的任何排序语义将被忽略。
请注意,自动检测的后处理器 (例如,在ApplicationContext中作为bean) 将始终在以编程方式注册的处理器之后应用。

void ignoreDependencyInterface(Class<?> ifc);这个方法:忽略给定的依赖接口进行自动查询。
这通常将由应用程序上下文用于注册依赖关系,这些依赖关系以其他方式解决,例如通过BeanFactoryAware的BeanFactory或通过ApplicationContext的ApplicationContext。
默认情况下,仅忽略BeanFactoryAware接口。对于要忽略的其他类型,请为每种类型调用此方法。

void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue);这个方法:用相应的autowired值注册一个特殊的依赖类型。这适用于factorycontext引用,这些引用应该是自动的,但在工厂中未定义为bean: 例如,ApplicationContext类型的依赖关系解析为bean所在的ApplicationContext实例。

注意: 在普通BeanFactory中没有注册这样的默认类型,甚至对于BeanFactory接口本身也没有。
Spring源码笔记之beanFactory.registerResolvableDependency()方法,这个文章里解释的很清楚,还有使用示例。

void registerSingleton(String beanName, Object singletonObject);这个方法:在给定的bean名称下,在bean注册表中将给定的现有对象注册为singleton。
给定的实例应该是完全初始化的; 注册表不会执行任何初始化回调 (特别是,它不会调用InitializingBean的afterPropertiesSet方法)。给定的实例也不会收到任何破坏回调 (如dispoablebean的destroy方法)。
在full BeanFactory中运行时: 如果您的bean应该接收初始化和/或销毁回调,请注册bean定义而不是现有实例。
通常在注册表配置期间调用,但也可以用于单例的运行时注册。因此,注册表实现应该同步单例访问; 如果它支持BeanFactory的单例延迟初始化,则无论如何都必须这样做

根据上面的铺垫,看下面就很顺畅。分为3部分

  • 表达式解析器
  • 添加应用上下文Aware的BeanPostProcessor
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// Tell the internal bean factory to use the context's class loader etc.
	// 将内部bean工厂使用上下文的类加载器等
	beanFactory.setBeanClassLoader(getClassLoader());
	// 忽略表达式
	if (!shouldIgnoreSpel) {
		// 设置Bean表达式解析器
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	}
	// 添加属性编辑器注册器
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

	// Configure the bean factory with context callbacks.
	// 添加上下文感知处理器
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
	// 忽略环境感知
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
	// 忽略内含价值解析器感知
	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
	// 忽略资源加载感知
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	// 忽略应用程序事件发布者感知
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	// 忽略消息资源感知
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	// 忽略应用上下文感知
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
	// 忽略应用启动步骤感知
	beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);

	// BeanFactory interface not registered as resolvable type in a plain factory.
	// MessageSource registered (and found for autowiring) as a bean.
	// 其他类想要用BeanFactory.class,就注入beanFactory。下面几个同理
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);

	// Register early post-processor for detecting inner beans as ApplicationListeners.
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

	// Detect a LoadTimeWeaver and prepare for weaving, if found.
	if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}

	// Register default environment beans.
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
	if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
		beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
	}
}

http://www.kler.cn/a/15246.html

相关文章:

  • 【经验分享】2024年11月下半年软件设计师考试选择题估分(持续更新~~)
  • AWD脚本编写_1
  • Ascend C算子性能优化实用技巧05——API使用优化
  • 智谱AI清影升级:引领AI视频进入音效新时代
  • MySQL —— MySQL索引介绍、索引数据结构、聚集索引和辅助索引、索引覆盖
  • C# DataTable使用Linq查询详解
  • 用友自主研发企业商用版TimensionDB时序数据库重磅发布!
  • 【算法】【算法杂谈】判断点是否在三角形内部(面积法和向量法)
  • 西门子S7-1500与FANUC机器人进行EtherNetIP通信的具体方法示例
  • 从0搭建Vue3组件库(九):VitePress 搭建部署组件库文档
  • 【华为OD机试真题】简单的解压缩算法(javapython) 100%通过率 超详细代码注释
  • 大数据Doris(三):Apache Doris分布式部署准备工作
  • <网络编程>网络套接字
  • Android那两个你碰不到但是很重要的类之ViewRootImpl
  • ToLua框架
  • 全国计算机等级三级网络技术试卷详解(二)
  • taro之项目初始化模板
  • JavaScript中的数据结构和算法
  • 一个朋友弄来的,太牛了,特别是后面内容,不看不知道,一看吓一跳,电话,热线
  • Leetcodes刷题之删除链表的倒数N个结点和删除链表的中间的结点
  • SD卡变成RAW格式怎么办?SD卡RAW格式的解决办法
  • HTML + CSS + JS 利用邮编查询 API 实现邮编查询工具
  • 【Mycat2】什么是原型库(Prototype)
  • 被遗忘的Java关键字:transient
  • 【刷题之路】LeetCode 203. 移除链表元素
  • Arduino学习笔记5