IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Boot 2.x源码系列【2】启动流程深入解析之SpringApplication类 -> 正文阅读

[Java知识库]Spring Boot 2.x源码系列【2】启动流程深入解析之SpringApplication类

有道无术,术尚可求,有术无道,止于术。

本系列Spring Boot版本2.7.0

前言

在使用Spring Boot开发应用时,我们只需要一个简单启动类,就能完成整个应用的加载启动,这个启动流程实际是很复杂,接下来我们就分几个篇章深入了解一下。

首先本篇会着重介绍SpringApplication类。

SpringApplication

SpringApplication翻译过来就是Spring 应用程序的意思,它的主要作用就是在JAVA main 方法中引导和启动Spring应用程序。

在源码中属于spring-boot 模块:
在这里插入图片描述

属性

SpringApplication的成员属性描述如下:

	// banner图的默认位置 banner.txt
	public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
	// 配置banner 位置的属性名称: spring.banner.location
	public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
	// 设置java.awt.headless模式 的属性名
	private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
	// 日志记录对象
	private static final Log logger = LogFactory.getLog(SpringApplication.class);
	//  关闭Spring Boot 应用程序钩子
	static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook();
	// 主类
	private Set<Class<?>> primarySources;
	// 创建 ApplicationContext 的其他资源,可以是:类名、包名或 XML 资源位置
	private Set<String> sources = new LinkedHashSet<>();
    // 主类的Class 类型
	private Class<?> mainApplicationClass;
	// Banner 图的模式,可以关闭开启
	private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
	// 是否 打印 Spring Boot 启动的时长日志
	private boolean logStartupInfo = true;
	// 是否通过命令行参数向application.properties中添加属性配置,比如 将java -jar 中设置的参数传递到 application.properties重
	private boolean addCommandLineProperties = true;
	// 是否添加类型转换服务
	private boolean addConversionService = true;
	// Banner 对象
	private Banner banner;
	// 资源加载器
	private ResourceLoader resourceLoader;
	// Bean名称生成器
	private BeanNameGenerator beanNameGenerator;
	// 配置属性环境
	private ConfigurableEnvironment environment;
	// Web 程序的类型,SERVLET、REACTIVE
	private WebApplicationType webApplicationType;
	// 设置headless 模式(告诉系统该程序没有显示器、鼠标能外设)
    this.headless = true;
    // 是否注册关闭钩子
	private boolean registerShutdownHook = true;
	// 设置ApplicationContextInitializer(可用在初始化的时候处理一些东西)
	private List<ApplicationContextInitializer<?>> initializers;
	// 监听器
	private List<ApplicationListener<?>> listeners;
	// 默认属性
	private Map<String, Object> defaultProperties;
	// 使用 {@link BootstrapRegistry} 之前对其进行初始化的回调接口
	private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
	// 附加配置文件
	private Set<String> additionalProfiles = Collections.emptySet();
	// 允许 Bean 覆盖
	private boolean allowBeanDefinitionOverriding;
	// 允许循环引用
	private boolean allowCircularReferences;
	// 是否自定义环境
	private boolean isCustomEnvironment = false;
	// 是否延迟初始化
	private boolean lazyInitialization = false;
	// 环境前缀
	private String environmentPrefix;
	// 应用上下文工厂,默认ConfigurableApplicationContext
	private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
	// 程序启动器
	private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;

在这些属性中,可以看到很多和环境、初始化、IOC容器、Banner、日志、钩子有关的配置。

构造函数

SpringApplication的构造函数,会初始化很多属性,我们在启动时,也可以自己创建对象并设置这些属性,实现自定义启动。

	// 参数
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {		
    	// 资源集合
        this.sources = new LinkedHashSet();
        // banner 图模式,可以设置在控制台、关闭、日志文件中显示
        this.bannerMode = Mode.CONSOLE;
        // 打印 Spring Boot 启动的时长日志
        this.logStartupInfo = true;
        // 外部命令
        this.addCommandLineProperties = true;
        // 对象间的转换的转换器
        this.addConversionService = true;
        // 设置headless 模式(告诉系统该程序没有显示器、鼠标能外设)
        this.headless = true;
        // 是否注册关闭钩子
        this.registerShutdownHook = true;
        //  配置额外的配置文件
        this.additionalProfiles = Collections.emptySet();
        // 是否自定义环境
        this.isCustomEnvironment = false;
        // 是否延迟初始化
        this.lazyInitialization = false;
        // 使用默认的IOC容器
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        // 检测应用程序启动阶段,监控启动时的各种性能指标
        this.applicationStartup = ApplicationStartup.DEFAULT;
        // 资源加载时,这里为null 
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 启动类,也就是主方法传去的参数
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // Web程序类型,    NONE,SERVLET(默认), REACTIVE;
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 初始化bootstrapRegistryInitializers引导程序注册的初始化器
        // 将spring.factories中key为BootstrapRegistryInitializer对应的类型实例化后放到成员变量initializers里(
        this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        //  将spring.factories中key为ApplicationContextInitializer对应的类型实例化后放到成员变量initializers里(调用默认构造函数,生成一个实例)
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 将spring.factories中key为ApplicationListener对应的类型实例化后放到成员变量Listeners里(调用默认构造函数,生成一个实例)
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // 推断出当前启动的main函数所在的类。
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

可以在Debug 中,看到改对象创建后加载的一系列属性,创建完成后就开始调用run 方法启动程序了。
在这里插入图片描述

run 方法

在大多数情况下,直接在main 方法中调用run 方法启动应用程序,run 方法会运行 Spring 应用程序,创建并刷新一个新的IOC 容器。

@SpringBootApplication
public class App1 {
    public static void main(String[] args) {
        SpringApplication.run(App1.class,args);      
    }
}

也可以先创建SpringApplication 实例,然后设置属性,再调用run 方法启动,以实现更高级的配置。

@SpringBootApplication
public class App1 {
    public static void main(String[] args) {
           SpringApplication application = new SpringApplication(MyApplication.class);
   		   // ... customize application settings here
   		   application.run(args)
    }
}

程序启动都是通过SpringApplication.run方法作为入口的,run方法会new一个SpringApplication,然后再调用对象的run方法。

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

接着调用到run(String... args)方法,Spring Boot在这里完成整个应用的加载、创建、运行,最后会返回一个IOC容器,具体的每一步流程后面介绍。

    public ConfigurableApplicationContext run(String... args) {
    	// 开始时间
        long startTime = System.nanoTime();
        // 1. 创建Bootstrap上下文
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        // 2. 设置系统参数 java.awt.headless
        this.configureHeadlessProperty();
        // 3. 获取监听器,每个监听器都开启一个独立的线程去执行监听
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
        	// 4. 封装命令行参数,并准备环境
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            // 5. 查找并设置配置文件信息
            this.configureIgnoreBeanInfo(environment);
            // 6. 打印 Banner
            Banner printedBanner = this.printBanner(environment);
            // 7. 创建应用上下文,并添加一个应用程序监视器
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // 8. 准备应用上下文
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // 9. 刷新应用上下文
            this.refreshContext(context);
            // 10.刷新完成后调
            this.afterRefresh(context, applicationArguments);
            // 11. 计算启动时间,JDK Duration类表示秒或纳秒时间间隔,
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            // 12. 打印启动日志
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
			// 13. 开始执行监听器
            listeners.started(context, timeTakenToStartup);
            // 14. 从应用上下文中获取ApplicationRunner和CommandLinerRunner类型的Bean并调用他们的run方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
        	// 处理异常
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            // 15. 准备监听器
            listeners.ready(context, timeTakenToReady);
            // 返回上下文
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

其他方法

SpringApplication 中,有1000多行代码,提供了很多私有方法,具体怎么执行的,后面一步步分析。
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-29 18:51:47  更:2022-06-29 18:51:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 10:06:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码