Spring源码十四:Spring生命周期

上一篇我们在Spring源码十三:非懒加载单例Bean中看到了Spring会在refresh方法中去调用我们的finishBeanFactoryInitialization方法去实例化,所有非懒加载器单例的bean。并实例化后的实例放到单例缓存中。到此我们refresh方法已经接近尾声。


Spring的生命周期

今天我们来看refresh方法中的最后一块逻辑:

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing. 1、初始化上下文信息,替换占位符、必要参数的校验prepareRefresh();// Tell the subclass to refresh the internal bean factory. 2、解析类Xml、初始化BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 这一步主要是对初级容器的基础设计// Prepare the bean factory for use in this context. 	3、准备BeanFactory内容:prepareBeanFactory(beanFactory); // 对beanFactory容器的功能的扩展:try {// Allows post-processing of the bean factory in context subclasses. 4、扩展点加一:空实现,主要用于处理特殊Bean的后置处理器postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context. 	5、spring bean容器的后置处理器invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation. 	6、注册bean的后置处理器registerBeanPostProcessors(beanFactory);// Initialize message source for this context.	7、初始化消息源initMessageSource();// Initialize event multicaster for this context.	8、初始化事件广播器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses. 9、扩展点加一:空实现;主要是在实例化之前做些bean初始化扩展onRefresh();// Check for listener beans and register them.	10、初始化监听器registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 11、实例化:非懒加载BeanfinishBeanFactoryInitialization(beanFactory);//!!!!!!!!!!!!  这里 这里 今天看这里  !!!!!!!!!!!//// Last step: publish corresponding event.// 12、初始化生命周期处理器,发布相应的事件通知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();}}}


finishRefresh

我们进入finishRefresh方法中去:

/*** Finish the refresh of this context, invoking the LifecycleProcessor's* onRefresh() method and publishing the* 完成应用上下文的刷新过程。此方法确保所有的资源和组件都已初始化,* 并发布相应的事件通知监听器* {@link org.springframework.context.event.ContextRefreshedEvent}.*/protected void finishRefresh() {// Clear context-level resource caches (such as ASM metadata from scanning).// 清除上下文级别的资源缓存clearResourceCaches();// Initialize lifecycle processor for this context.// 初始化化生命周期处理器initLifecycleProcessor();// Propagate refresh to lifecycle processor first.getLifecycleProcessor().onRefresh();// Publish the final event.// 发布已经刷新完成的事件publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.// 将Application容器注册到LiveBeanView中LiveBeansView.registerApplicationContext(this);}

上述注释已经比较完整了,finishRefresh方法我们在整理总结下:主要是初始化和生命周期相关的一些组件,我们可以先到initLifecycleProcessor的方法,看下具体做了些什么?

/*** 初始化生命周期组件,如果beanFactory没有定义,* 则使用DefaultLifecycleProcessor初始化一个* 默认的生命周期组件,注册到容器中* Initialize the LifecycleProcessor.* Uses DefaultLifecycleProcessor if none defined in the context.* @see org.springframework.context.support.DefaultLifecycleProcessor*/protected void initLifecycleProcessor() {// 获取BeanFactory容器ConfigurableListableBeanFactory beanFactory = getBeanFactory();// 判断容器中是否存在lifecycleProcessorif (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {// 获取lifecycleProcessor,注册当前容器(AbstractApplicationContext)this.lifecycleProcessor =beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);if (logger.isTraceEnabled()) {logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");}}else {// 如果容器中没有lifecycleProcessor,则初始化DefaultLifecycleProcessorDefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();defaultProcessor.setBeanFactory(beanFactory);// 设置成员属性this.lifecycleProcessor = defaultProcessor;// 注册到Spring容器中beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);if (logger.isTraceEnabled()) {logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +"[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");}}}

可以看到在方法initLifecycleProcessor中,首先看下beanFactory中是否有名称为lifecycleProcessor的bean,存在直接获并复制给abstractApplicationContext的成员变量lifecycleProcessor,如果不存在,Spring框架会自动创建DefaultLifecycleProcessor类型的对象,赋值给成员变量,最后再注入到Spring容器中


DefaultLifecycleProcessor

进入DefaultLifecycleProcessor类中:

可以看到它主要实现了LifecycleProcessor, BeanFactoryAware这两个接口,我们来看下它的类图,如下所示:

看左边这块,DefaultLifecycleProcessor最终也是实现了Lifecycle接口与LifecycleProcessor接口我们展开看下这两个接口:

在Spring中,如果实现了接口Lifecycle,Spirng会在容器启动时,调用这些Bean中的start方法开启他们的生命周期,同样:如果在Spring容器关闭时也会调用stop方法来结束他们的生命周期。

而LifecycleProcessor在此基础上又新增了onRefrsh方法和onClose方法,主用来处理对应Bean的状态。

方法调用如下:


// 	// Propagate refresh to lifecycle processor first.//		getLifecycleProcessor().onRefresh();/*** 调用DefaultLifecycleProcessor.onRefresh()*/@Overridepublic void  onRefresh() {startBeans(true);this.running = true;}// Internal helpersprivate void startBeans(boolean autoStartupOnly) {// 1、获取所有lifecycle类型的BeanMap<String, Lifecycle> lifecycleBeans = getLifecycleBeans();Map<Integer, LifecycleGroup> phases = new HashMap<>();lifecycleBeans.forEach((beanName, bean) -> {if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {int phase = getPhase(bean);LifecycleGroup group = phases.get(phase);if (group == null) {group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);phases.put(phase, group);}group.add(beanName, bean);}});// 遍历 Map,排序后启动if (!phases.isEmpty()) {List<Integer> keys = new ArrayList<>(phases.keySet());Collections.sort(keys);for (Integer key : keys) {phases.get(key).start();}}}

可以看到在onRefresh方法中,像我们刚才分析的一样,就会从Spring容器中获取所有实现了Lifecycle接口的bean,然后调用start方法来开启它们的生命周期,也就是开启Spring的生命周期了。


publishEvent

接着往下看:

		// 发布已经刷新完成的事件publishEvent(new ContextRefreshedEvent(this));/*** Publish the given event to all listeners.* 发布事件,通知所有监听器* <p>Note: Listeners get initialized after the MessageSource, to be able* to access it within listener implementations. Thus, MessageSource* implementations cannot publish events.* @param event the event to publish (may be application-specific or a* standard framework event)*/@Overridepublic void publishEvent(ApplicationEvent event) {publishEvent(event, null);}/**** Publish the given event to all listeners.* 					用于将指定的事件发布给所有监听器* @param event the event to publish (may be an {@link ApplicationEvent}*                 	参数表示要发布的事件,可以是 ApplicationEvent* or a payload object to be turned into a {@link PayloadApplicationEvent})*               	或一个负载对象(将被转换为 PayloadApplicationEvent)。* 	 ** @param eventType the resolved event type, if known* @since 4.2*/protected void publishEvent(Object event, @Nullable ResolvableType eventType) {Assert.notNull(event, "Event must not be null");// Decorate event as an ApplicationEvent if necessary// 判断传入的 event 是否为 ApplicationEvent 的实例://		如果是,则直接赋值给 applicationEvent。//		如果不是,则将其包装为 PayloadApplicationEvent。ApplicationEvent applicationEvent;if (event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;}else {applicationEvent = new PayloadApplicationEvent<>(this, event);// 如果 eventType 为空且事件被包装为 PayloadApplicationEvent,则从包装的事件中获取事件类型if (eventType == null) {eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}// 事件分发// Multicast right now if possible - or lazily once the multicaster is initialized:// 判断 earlyApplicationEvents 是否不为空// 如果不为空,说明广播器还未初始化,则将事件添加到 earlyApplicationEvents 队列中,等待广播器初始化后再分发。// 如果为空,说明广播器已初始化, 立即分发事件。if (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else {// 直接通过 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}// Publish event via parent context as well...// 如果当前上下文有父上下文 (this.parent 不为空),则同样向父上下文发布事件:if (this.parent != null) {//如果父上下文是 AbstractApplicationContext 的实例,则调用其 publishEvent 方法,同时传递事件和事件类型。// 如果不是,则直接调用父上下文的 publishEvent 方法。if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);}else {this.parent.publishEvent(event);}}}

根据我们两篇关于事件驱动的源码分析,事件ContextRefreshedEvent将会注册到广播器中,广播器会把该事件广播器相应的监听器去处理,这个事件相当于告诉Spring整个容器已经刷新了,也就说Spring容器ApplicationContext已经初始化完毕了。

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1473585.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

提升系统稳定性:熔断、降级和限流策略详解

文章目录 前言一、熔断&#xff08;Circuit Breaker&#xff09;二、降级&#xff08;Degradation&#xff09;三、限流&#xff08;Rate Limiting&#xff09;四、应用案例五、小结推荐阅读 前言 随着互联网业务的快速发展&#xff0c;系统稳定性和高可用性成为现代分布式系统…

【Python机器学习】算法链与管道——网格搜索预处理步骤与模型参数

我们可以利用管道将机器学习工作流程中的所有处理步骤封装成一个scikit-learn估计器。这么做的好处在于&#xff1a;现在我们可以使用监督任务&#xff08;分类或回归&#xff09;的输出来调节预处理参数。 下面用一个管道来完成一个建模过程。管道包含了3个步骤&#xff1a;缩…

ELK优化之Filebeat部署

目录 1.安装配置Nginx 2.安装 Filebeat 3.设置 filebeat 的主配置文件 4.修改Logstash配置 5.启动配置 6.kibana验证 主机名ip地址主要软件es01192.168.9.114ElasticSearches02192.168.9.115ElasticSearches03192.168.9.116ElasticSearch、Kibananginx01192.168.9.113ng…

【C语言】register 关键字

在C语言中&#xff0c;register关键字用于提示编译器将变量尽量存储在CPU的寄存器中&#xff0c;而不是在内存中。这是为了提高访问速度&#xff0c;因为寄存器的访问速度比内存快得多。使用register关键字的变量通常是频繁使用的局部变量。 基本用法 void example() {regist…

iOS多target时怎么对InfoPlist进行国际化

由于不同target要显示不同的App名称、不同的权限提示语&#xff0c;国际化InfoPlist文件必须创建名称为InfoPlist.strings的文件&#xff0c;那么多个target时怎么进行国际化呢&#xff1f;步骤如下&#xff1a; 一、首先我们在项目根目录创建不同的文件夹对应多个不同的targe…

自闭症儿童的治疗方法有哪些?

身为星贝育园自闭症儿童康复学校的资深教育者&#xff0c;我深知自闭症谱系障碍&#xff08;ASD&#xff09;儿童的教育与治疗需要一个全面、个性化的方案。在星贝育园&#xff0c;我们致力于为孩子们提供一个充满爱与理解的环境&#xff0c;采用多种科学验证的教育方法&#x…

商贸物流大脑:大模型+数据要素赋能智慧物流数据平台

项目背景与意义 物流行业快速发展&#xff0c;数据量急剧增加&#xff0c;随着电子商务、智能制造等领域的快速发展&#xff0c;物流行业面领着前所未有的挑战和机遇&#xff0c;如效率低下、资源配置不均、信息不透明等问题。随着全球化和电子商务的快速发展&#xff0c;数据…

Arthas实战(5)- 项目性能调优

1、接口耗时查询&#xff1a;trace命令 trace 命令能主动搜索 class-pattern&#xff0f;method-pattern 对应的方法调用路径&#xff0c;渲染和统计整个调用链路上的所有性能开销和追踪调用链路。 1.1 准备测试应用 新建一个 SpringBoot 应用&#xff0c;写一耗时久的代码&…

编写优雅Python代码的20个最佳实践

想要让你的代码像艺术品一样既实用又赏心悦目吗&#xff1f;今天我们就来聊聊如何通过20个小技巧&#xff0c;让你的Python代码从平凡走向优雅&#xff0c;让同行看了都忍不住点赞&#xff01; **温馨提示&#xff1a;更多的编程资料&#xff0c;领取方式在&#xff1a; 1. 拥…

《基于 defineProperty 实现前端运行时变量检测》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;欢迎多多交流~ &am…

209.力扣每日一题:质数的最大距离

代码解决 class Solution { public:// 判断一个数是否为质数bool isPrime(int n) {if (n < 1) {return false;}if (n < 3) {return true;}if (n % 2 0 || n % 3 0) {return false;}for (int i 5; i * i < n; i 6) {if (n % i 0 || n % (i 2) 0) {return false;…

C生万物之文件操作

文章目录 一、为什么使用文件&#xff1f;二、什么是文件&#xff1f;1、程序文件2、数据文件3、文件名 三、文件的打开和关闭1、文件指针2、文件的打开和关闭 四、文件的顺序读写1. 8个重要的库函数1.1 单字符输入输出【fputc和fgetc】1.2 文本行输入输出【fputs和fgets】1.3 …

API Object设计模式

API测试面临的问题 API测试由于编写简单&#xff0c;以及较高的稳定性&#xff0c;许多公司都以不同工具和框架维护API自动化测试。我们基于seldom框架也积累了几千条自动化用例。 •简单的用例 import seldomclass TestRequest(seldom.TestCase):def test_post_method(self…

固态,机械,移动(U盘),sd卡,哪个更适合长期储存数据 保存数据用什么硬盘可靠 硬盘数据丢失怎么找回 硬盘维护注意事项

有关硬盘数据丢失的恢复技巧&#xff0c;这篇文章一定要收藏好。在硬盘使用过程中&#xff0c;很多情况都会导致数据丢失&#xff0c;例如硬盘跌落、病毒感染、系统文件损坏等。这时候&#xff0c;一定要采用正确的方法&#xff0c;抢救硬盘中存储的珍贵数据和文档。 有关长期保…

如何选择最佳的照片和视频恢复软件

您是否意外从硬盘或 USB 卡中删除了照片或视频&#xff1f;最好的视频和照片恢复软件可以帮到您&#xff01;如果您一直在寻找最好的照片恢复软件&#xff0c;那么您来对地方了。本文将分享一些帮助您找到最佳视频恢复软件的提示。 重要提示&#xff1a;事实&#xff1a;媒体文…

【Qt5.12.9】程序无法显示照片问题(已解决)

问题记录&#xff1a;Qt5.12.9下无法显示照片 我的工程名为03_qpainter&#xff0c;照片cd.png存放在工程目录下的image文件夹中。 /03_qpainter/image/cd.png 因为这是正点原子Linux下Qt书籍中的例程&#xff0c;在通过学习其配套的例程中的项目&#xff0c;发现我的项目少…

广州星启帆:点亮自闭症儿童康复之路的璀璨星辰

在广州这座充满温情的城市中&#xff0c;广州星启帆自闭症康复中心如同一颗璀璨的星辰&#xff0c;照亮了无数自闭症儿童及其家庭的前行之路。这家机构以“点亮希望&#xff0c;启航未来”为使命&#xff0c;向所有踏入这里的家庭承诺&#xff1a;我们将携手并肩&#xff0c;共…

非平稳信号的自适应局部迭代滤波(MATLAB)

仍以滚动轴承故障诊断为例&#xff0c;在滚动轴承的运行过程中&#xff0c;其振动信号包含了大量的系统运行状态信息。利用振动信号进行滚动轴承的故障诊断&#xff0c;实际上就是分析振动信号、提取信息的过程。由于非线性力的作用&#xff0c;滚动轴承的故障信号往往是非平稳…

记录一次麒麟V10 安装sysbench各种报错(关于MySQL)处理过程

sysbench手工下载&#xff1a; https://github.com/akopytov/sysbench 下载.zip文件&#xff0c;上传到服务器上 解压、安装&#xff1a; unzip sysbench-master.zipcd sysbench-master/sh autogen.sh./configure 报错&#xff1a;没有mysql驱动 configure: error: mysql_c…

UEC++ 虚幻5第三人称射击游戏(二)

UEC++ 虚幻5第三人称射击游戏(二) 派生榴弹类武器 新建一个继承自Weapon的子类作为派生榴弹类武器 将Weapon类中的Fire函数添加virtual关键字变为虚函数让榴弹类继承重写 在ProjectileWeapon中重写Fire函数,新建生成投射物的模版变量 Fire函数重写逻辑 代码//生成的投射物U…