带着这两点看Springboot源码

简介: 带着这两点看Springboot源码

不积跬步无以至千里, 量变才是质量的基础。

一、Spring SPI二、伴随事件的生命周期1.前期准备(SPI)2.事件3.启动过程三、总结

理解Springboot原理,个人认为可以从两个点来入手

  • Spring SPI
  • 伴随事件的生命周期

版本说明:springboot 项目下直接阅读:Spring Boot:2.2.2.RELEASE


一、Spring SPI


你如果知道JAVA SPI ,或者Dubbo SPI 。那么你的脑海里一定对Spring SPI 的机制有了一个大概的轮廓了。

SPI: 为某个接口寻找服务实现的机制,按约定,从指定文件寻找配置的接口实现

SpringFactoriesLoader

我们来看看Springboot中SPI的工具类的SpringFactoriesLoader

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            // 【1】取得资源文件的URL
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                //【2】 将资源文件解析为`Properties 
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                //【3】 取出配置的value值
                String propertyValue = properties.getProperty(factoryClassName);
                for (String factoryName : StringUtils.commaDelimitedListToStringArray(propertyValue)) {
                    result.add(factoryName.trim());
                }
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

META-INF/spring.factories文件内容格式


image.png


# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

工作原理:

  • 获取搜寻CLASSPATH下所有的META-INF/spring.factories资源文件的URL
  • 遍历URL,将资源文件解析为Properties
  • 以传入的factoryClass为KEY,获取资源文件里配置的Value值

总结为:根据传入的factoryClass,从资源文件META-INF/spring.factories中获取对应的value的过程

例如:

  • 当我们调用SpringFactoriesLoader.loadFactoryNames(ApplicationContextInitializer.class, this.beanClassLoader);
  • 就是以ApplicationContextInitializer为key从资源文件META-INF/spring.factories获得SharedMetadataReaderFactoryContextInitializerAutoConfigurationReportLoggingInitializer

本质是一种从文件加载配置的机制。

Springboot之所以,以减少开发人员的配置出名,其功劳有Spring SPI的一部分。因为其很多默认的配置,都是配置在资源文件META-INF/spring.factories中,并以EnableAutoConfiguration为KEY ,经由SPI工具类SpringFactoriesLoader 加载到上下文中。

所以说:理解Spring SPI也就明白了Springboot自动化配置原理了。


二、伴随事件的生命周期


Springboot生命起源于SpringApplication.run方法


1.前期准备(SPI)

  • 判断当前环境是不是web环境
  • 通过SPI机制将资源文件中配置ApplicationContextInitializer接口(扩展点)与ApplicationListener接口(监听器)的实现类全限定名,获取到。
  • 通过SPI机制 获取SpringApplicationRunListener接口的实现,封装成一个SpringApplicationRunListeners(有s,组的概念)。其中一个实现EventPublishingRunListener最常用,一句话说明白:他就是一个广播器,持有准备阶段获取的监听器,用于发布广播阶段事件到事件监听器。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener


2.事件

伴随着生命周期有哪些事件呢?监听器监听的是哪些事件?


image.png

image.png


  • ApplicationStartingEvent : 应用启动中
  • ApplicationEnvironmentPreparedEvent:应用环境准备好
  • ApplicationContextInitializedEvent:应用上下文已实例化
  • ApplicationPreparedEvent :上下文准备好
  • ApplicationStartedEvent:应用成功启动
  • ApplicationReadyEvent:应用已准备好
  • ApplicationFailedEvent :应用启动失败

完整的什么周期,清晰指出了每个重要的时间点。从事件我们也大致可以看出Springboot的原理了。

顺便提一句,在低版本的springboot里其实没有这么多事件,比如一些版本在listeners.starting();看起方法应该是启动中,但是发布的事件竟然是ApplicationStartedEvent,当时我就觉得别扭。直到看了高版本,发现listeners.starting();发布的就是ApplicationStartingEvent,非常的舒服。不别扭了。


3.启动过程

都在run方法里了。

3.1、应用开始启动

发布应用启动中【1】ApplicationStartingEvent事件,表示应用开始启动了,所有监听此事件的监听器,做出反应

listeners.starting();
===============
public void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }


3.2、创建一个ConfigurableEnvironment实例,表示当前环境。

发送【2】ApplicationEnvironmentPreparedEvent事件,触发对应的监听器的执行

此处提一个监听器:BootstrapApplicationListener监听器,此监听器是来自SpringCloud ,用于启动/创建Spring Cloud的应用上下文。SpirngCloud的上下文优先SpringBoot的上下文创建。

这对于我们理解SpringCloud,非常关键。


3.3、打印Banner

Banner printedBanner = printBanner(environment);

我们各种Banner就是在这打印出来的


3.4、创建上下文实例。

根据当前环境,创建出一个上下文实例对象出来。

context = createApplicationContext();
=====
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
switch (this.webApplicationType) {
    case SERVLET:
        contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
。。。。
}


3.5、设置上下文实例,并执行ApplicationContextInitializer(准备阶段获取的),单例Bean是仍旧还没有初始化,发布【3】ApplicationContextInitializedEvent事件,表示上下文准备好了。


3.6、加载早期BeanDefinition,发送【4】ApplicationPreparedEvent事件,触发对应的监听器的执行

load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);


3.7、refreshContext,这里最重要的是执行了refresh(context); 实例化了Bean。这个方法执行完成,基本上主要工作就完成了。

发送【5】ApplicationStartedEvent事件,触发对应的监听器的执行

refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass)
        .logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);


3.8、Springboot留的扩展点

callRunners(context, applicationArguments);


3.9、至此Springboot基本上就完成了,发布【6】ApplicationReadyEvent事件。应用准备好了

try {
    listeners.running(context);
}catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, null);
    throw new IllegalStateException(ex);
}
return context;


3.10、中间出错了会发布ApplicationFailedEvent应用启动失败事件。

handleRunFailure(context, ex, exceptionReporters, null);

Springboot的启动过程大致流程就是这样。

总结为:这是一个应用启动,准备,创建上下文,完成上下文设置,应用完成,应用完成收尾的过程,伴随着不同阶段的事件发布


三、总结


Springboot抓住SPI与伴随事件的生命周期,这两点,理解起来,就很方便了

Springboot本身又是对Spring的一次封装,其工作原理无非就是围绕Spring的核心做工作。伴随的事件,给我留下了无限扩扩展点。


相关文章
|
7天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
2天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
1月前
|
JavaScript Java 关系型数据库
美妆商城系统 SpringBoot + Vue 【毕业设计 资料 + 源码】
这篇文章介绍了一个使用SpringBoot + Vue + Mybatis + Mysql技术栈开发的美妆商城系统,包括系统功能划分、部分页面截图和前后端源码示例,并提供了GitHub上的源码链接。
美妆商城系统 SpringBoot + Vue 【毕业设计 资料 + 源码】
|
29天前
|
安全 Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+jsp实现的健身房管理系统(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和JSP技术实现的健身房管理系统。随着健康生活观念的普及,健身房成为日常锻炼的重要场所,高效管理会员信息、课程安排等变得尤为重要。该系统旨在通过简洁的操作界面帮助管理者轻松处理日常运营挑战。技术栈包括:JDK 1.8、Maven 3.6、MySQL 8.0、JSP、Shiro、Spring Boot 2.0等。系统功能覆盖登录、会员管理(如会员列表、充值管理)、教练管理、课程管理、器材管理、物品遗失管理、商品管理及信息统计等多方面。
|
27天前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的考试管理系统(含教程&源码&数据库数据)
在数字化时代背景下,本文详细介绍了如何使用Spring Boot框架结合Vue.js技术栈,实现一个前后端分离的考试管理系统。该系统旨在提升考试管理效率,优化用户体验,确保数据安全及可维护性。技术选型包括:Spring Boot 2.0、Vue.js 2.0、Node.js 12.14.0、MySQL 8.0、Element-UI等。系统功能涵盖登录注册、学员考试(包括查看试卷、答题、成绩查询等)、管理员功能(题库管理、试题管理、试卷管理、系统设置等)。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的考试管理系统(含教程&源码&数据库数据)
|
1月前
|
Web App开发 前端开发 关系型数据库
基于SpringBoot+Vue+Redis+Mybatis的商城购物系统 【系统实现+系统源码+答辩PPT】
这篇文章介绍了一个基于SpringBoot+Vue+Redis+Mybatis技术栈开发的商城购物系统,包括系统功能、页面展示、前后端项目结构和核心代码,以及如何获取系统源码和答辩PPT的方法。
|
1月前
|
JavaScript Java Maven
毕设项目&课程设计&毕设项目:springboot+vue实现的在线求职管理平台(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和Vue.js实现的在线求职平台。该平台采用了前后端分离的架构,使用Spring Boot作为后端服务
毕设项目&课程设计&毕设项目:springboot+vue实现的在线求职管理平台(含教程&源码&数据库数据)
|
1月前
|
NoSQL JavaScript 前端开发
SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
文章介绍了如何使用SpringBoot和Vue实现一个校园二手系统,采用前后端分离技术。系统具备完整的功能,包括客户端和管理员端的界面设计、个人信息管理、商品浏览和交易、订单处理、公告发布等。技术栈包括Vue框架、ElementUI、SpringBoot、Mybatis-plus和Redis。文章还提供了部分源代码,展示了前后端的请求接口和Redis验证码功能实现,以及系统重构和模块化设计的一些思考。
SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
|
1月前
|
NoSQL 关系型数据库 MySQL
SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘
SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘
80 2
|
2月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的房屋租赁App的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的房屋租赁App的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的房屋租赁App的详细设计和实现(源码+lw+部署文档+讲解等)