SpringBoot启动原理和源码初步刨析

1、SpringBoot启动的概念、流程

SpringBoot并没有抛弃Spring,只是在Spring上面又维护了一些更方便的东西,底层还是Spring,类似于下面的图所示:

在这里插入图片描述

在这里插入图片描述

2、SpringBoot启动源码初步刨析

1、SpringBoot的启动原理,我们肯定是要从下面这个方法进入的:

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

2、进入run方法之后,代码如下所示:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	// 先new一个主配置类,然后再运行run方法
    return (new SpringApplication(primarySources)).run(args);
}

2.1、初始化SpringApplication类

1、当我们点击SpringApplication的时候,会发现调用了一个构造方法,如下所示:

public SpringApplication(Class<?>... primarySources) {
    this((ResourceLoader)null, primarySources);
}

2、点击this进入之后,代码如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 设置一些属性
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = Collections.emptySet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 将启动类放入 primarySources 
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    // 根据classPath下面的类,推算当前 web 应用
    // 推算当前web应用类型(webFlux,servlet)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
    // 去 classpath路径下找 spring.factories 文件中获取key:org.springframework.context.ApplicationContextInitializer
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 去 classpath路径下找 spring.factories 文件中获取key:org.springframework.context.ApplicationListener
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    // 根据main方法推算出mainApplicationClass 
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

上面获取的org.springframework.context.ApplicationContextInitializer

在这里插入图片描述

上面获取的org.springframework.context.ApplicationListener

在这里插入图片描述
总结:

实例化SpringApplication类的步骤
1、获取启动类放在this.primarySources中
2、获取web应用类型
3、读取了对外扩展的ApplicationContextInitializer和ApplicationListener
4、根据main推算出所在的类

2.2、执行run方法

1、点击进入run方法,代码如下所示:

public ConfigurableApplicationContext run(String... args) {
	// 记录SpringBoot的启动耗时时间
    StopWatch stopWatch = new StopWatch();
    // 启动起始时间
    stopWatch.start();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    // 它是任何 spring 上下文的接口,所以可以接收任何 ApplicationContext 的实现
    ConfigurableApplicationContext context = null;
    // 开启了 Headless 模式,
    // 这个Headless相当于应用部署到了某个服务器上时需要依赖于某些硬件设备
    // 但是现在这个服务器上又缺少某个硬件设备,Headless就会提供一些基本的映射配置
    this.configureHeadlessProperty();
    // 去 classpath路径下找 spring.factories 文件中获取key:org.springframework.boot.SpringApplicationRunListener
    // 启动监听器所用
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    // 启动监听器/发布器
    // 跟进去启动了 ApplicationStartingEvent 事件监听器,走2.2.1
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
    	// 根据命令行参数 实例化一个ApplicationArguments 
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 预初始化环境:读取环境变量,读取配置文件信息(基于监听器)
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // 忽略实现了 BeanInfo 接口的Bean
        this.configureIgnoreBeanInfo(environment);
        // 打印Banner横幅
        Banner printedBanner = this.printBanner(environment);
        // 根据 webApplicationType 创建Spring上下文
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 预初始化上下文,走2.2.3
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 加载Spring Ioc容器,springBoot在Spring上做了扩展
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
		// SpringBoot第五、六个公告
        listeners.started(context);
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, listeners);
        throw new IllegalStateException(var10);
    }

    try {
    	// SpringBoot的第七、八个公告
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
    	// SpringBoot的第九个公告
        this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

2.2.1、listeners.starting

1、进入listeners.starting中,跟到了这里,找到了发布ApplicationStartingEvent事件监听器的地方。

public void starting(ConfigurableBootstrapContext bootstrapContext) {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}

2、进入到multicastEvent,发布的代码如下:

public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    Executor executor = this.getTaskExecutor();
    Iterator var5 = this.getApplicationListeners(event, type).iterator();
	// 获取到当前的事件监听器,对所有的监听方法进行回调
    while(var5.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            this.invokeListener(listener, event);
        }
    }

}

2.2.2、this.prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	// 根据 webApplicationType 创建 Environment,创建就会读取:Java环境变量和系统变量
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    // 将上一步拿出来的命令行参数读取到环境变量中
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    // 将@PropertiesSource的配置信息 放在第一个,
    // 因为读取配置文件@PropertiesSource的优先级最低,后面的会覆盖前面的
    ConfigurationPropertySources.attach((Environment)environment);
    // ApplicationEnvironmentPreparedEvent公告的发布
    listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
    DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
    Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
    // 将spring.main配置开头的全局配置绑定到当前的SpringApplication类中
    this.bindToSpringApplication((ConfigurableEnvironment)environment);
    if (!this.isCustomEnvironment) {
        environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    }
	// 更新@PropertiesSource
    ConfigurationPropertySources.attach((Environment)environment);
    return (ConfigurableEnvironment)environment;
}

2.2.3、this.prepareContext

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    this.postProcessApplicationContext(context);
    // 拿到之前读取的所有 ApplicationContextInitializer 组件
    // 循环调用 initialize 方法
    this.applyInitializers(context);
    // 发布的第三个公告,ApplicationContextInitializedEvent
    listeners.contextPrepared(context);
    bootstrapContext.close(context);
    if (this.logStartupInfo) {
        this.logStartupInfo(context.getParent() == null);
        this.logStartupProfileInfo(context);
    }

	// 获取当前spring上下文beanFactory(负责创建Bean)
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }

	// 在Spring下  如果出现两个重名的 Bean,则后面的会覆盖掉前面的
	// SpringBoot中,在这里设置了不允许覆盖,如果出现重名,会抛出异常
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }

	// 设置当前spring容器是不是要将所有的bean设置为懒加载
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }

    Set<Object> sources = this.getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 读取主启动类,加载配置
    this.load(context, sources.toArray(new Object[0]));
    // SpringBoot的第四个公告
    listeners.contextLoaded(context);
}

发布的公告顺序

1、ApplicationStartingEvent运行开始时发送,但在进行任何处理之前发送。
默认的监听器

在这里插入图片描述

2、ApplicationEnvironmentPreparedEvent,在创建上下文之前。
默认的监听器

在这里插入图片描述

3、ApplicationContextInitializedEvent,准备ApplicationContext并调用ApplicationContextInitializer 之后。
4、ApplicationPreparedEvent,读取完配置类之后发送。
5、ApplicationStartedEvent,在加载完ioc容器之后。
6、AvailabilityChangeEvent,以指示该程序为活动状态。
7、ApplicationReadyEvent,调用任何程序和命令行运行程序之后。
8、AvailabilityChangeEvent,以指示程序已准备就绪,可以处理请求
9、ApplicationFailedEvent,如果启动时报错,则发送。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值