Spring Boot启动原理源码剖析

一、springboot启动原理及相关流程概览

springboot是基于spring的新型的轻量级框架,最厉害的地方当属自动配置。那我们就可以根据启动流程和相关原理来看看,如何实现传奇的自动配置

二、springboot的启动类入口 

用过springboot的技术人员很显而易见的两者之间的差别就是视觉上很直观的:springboot有自己独立的启动类(独立程序)

@SpringBootApplication
public class SpringbootWebApplication {

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

}

从启动类看,主要是两部分,一个是@SpringBootApplication注解,另一个是SpringApplication.run方法,下面就看这两个地方。

三、@SpringBootApplication源码分析

@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

在其中比较重要的有三个注解,分别是:

1)@SpringBootConfiguration 

Spring Boot的配置类;标注在某个类上,表示一个类提供了Spring Boot应用程序

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

@Configuration:配置类上来标注这个注解; 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

注意

配置类相当于配置文件;配置类也是容器中的一个组件,它使用了@Component这个注解。

2)@EnableAutoConfiguration 

告诉SpringBoot开启自动配置功能,这样自动配置才能生效
借助@import,扫描并实例化满足条件的自动配置的bean,然后加载到IOC容器中

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

@AutoConfigurationPackage:自动配置包
@Import(EnableAutoConfigurationImportSelector.class):给容器中导入组件

在这里插入图片描述 

使用@EnableAutoConfiguration
这个注解开启自动扫描,然后使用select选择挑选满足条件的文件,并且使用SpringFactoriesLoader进行实例化。最后加载到IOC容器里面,即ApplicationContext中。 

3)@ComponentScan

@ComponentScan就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IOC容器中去 。

 关于springboot的自动配置原理,我在另外一篇文章有详细的源码解析,以上三个注解这里就不细说了,可以看一下这篇文章:Spring Boot的自动配置原理

四、实例化SpringApplication对象的源码剖析

我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。

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

创建SpringApplication (new SpringApplication)

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 初始化资源加载器
        this.resourceLoader = resourceLoader;
        // 资源加载类不能为 null
		Assert.notNull(primarySources, "PrimarySources must not be null");
        // 初始化加载资源类集合并去重
        // 将启动类放入primarySources
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 推断应用程序是不是web应用
        // 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
        // 设置初始化器(Initializer)
        // 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 设置监听器 
        //就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 推断出主应用入口类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

1.推算web应用类型

        在推断应用程序是不是web应用的时候调用了deduceFromClasspath() 方法

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

	private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

	private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            // springboot2.0提出的响应式web应用
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
            // 如果两个包路径都没有的话,就是普通应用
			if (!ClassUtils.isPresent(className, null)) {
                // 普通的应用
				return WebApplicationType.NONE;
			}
		}
        // 其实最后返回的就是这个servlet,因为是web应用
		return WebApplicationType.SERVLET;
	}

2.设置初始化器(Initializer)

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

initializers 是 SpringApplication 中的一个实例属性

public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
		this.initializers = new ArrayList<>(initializers);
	}

initailizer实现了ApplicationContextInitializer接口

@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);

}

ApplicationContextInitializer接口的作用,在Spring上下文被刷新之前进行初始化的操作。典型地比如在Web应用中,注册Property Sources或者是激活Profiles。Property Sources比较好理解,就是配置文件。Profiles是Spring为了在不同环境下(如DEV,TEST,PRODUCTION等),加载不同的配置项而抽象出来的一个实体。
调用initialize()方法,把初始化的ApplicationContextInitializer实现加载到SpringApplication中

通过getSpringFactoriesInstances(
ApplicationContextInitializer.class)方法获得实现类

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	// 使用 Set保存names
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	// 根据names进行实例化
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	// 对实例进行排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

其中SpringFactoriesLoader.loadFactoryNames(type, classLoader)和自动配置一样读取META-INF/spring.factories然后拿到key为org.springframework.context.ApplicationContextInitializer的集合

其中一个spring.factories文件

获取到的所有ApplicationContextInitializer

https://note.youdao.com/yws/public/resource/a1475b8cffccdffcd309dd2a11832a35/xmlnote/5B4B1D97F26C4B9F80DD89797DF486A6/6EFE9FFA18464E5395A89AF8CAFB9BA7/12015

 3.设置监听器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

同理,也是一样去读取META-INF/spring.factories然后拿到key为org.springframework.context.ApplicationListener的集合

其中一个spring.factories文件

 获取到的所有ApplicationListener

https://note.youdao.com/yws/public/resource/a1475b8cffccdffcd309dd2a11832a35/xmlnote/5B4B1D97F26C4B9F80DD89797DF486A6/D6FB6783A11F423E986450B2D9125C5A/12020

 4.推断主应用入口类

private Class<?> deduceMainApplicationClass() {
	try {
		// 堆栈信息--调用链,执行链
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			// 通过main的栈帧推断出入口类的名字
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

 总结:

1. 获取启动类

2.获取web应用类型

3.读取了对外扩展的ApplicationContextInitializer ,ApplicationListener

4. 根据main推算出所在的类

就是去初始化了一些信息

五、run() 方法源码剖析

public ConfigurableApplicationContext run(String... args) {
        // 记时器,统计应用启动的时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        // 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现
        ConfigurableApplicationContext context = null;
        // 开启了Headless模式
        configureHeadlessProperty();
        // 去spring.factroies中读取了SpringApplicationRunListener 的组件,  就是用来发布事件或者运行监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 发布ApplicationStartingEvent事件,在运行开始时发送
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
        // 根据命令行参数 实例化一个ApplicationArguments
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // 忽略beaninfo的bean
        configureIgnoreBeanInfo(environment);
        // 打印器,springboot启动的时候会打印springboot的标志以及对应的版本
        Banner printedBanner = printBanner(environment);
        // 根据webApplicationType创建Spring上下文
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        //预初始化spring上下文
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 加载spring ioc 容器   **相当重要   由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
        //  加载自动配置类:invokeBeanFactoryPostProcessors ,  创建servlet容器onRefresh
        refreshContext(context);
        // Spring上下文后置处理
        afterRefresh(context, applicationArguments);
        // 停止计时器
        stopWatch.stop();
        if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 发布应用上下文启动完成事件
        listeners.started(context);
        // 执行所有 Runner 运行器
        callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
        }

        try {
        // 发布应用上下文就绪事件
        listeners.running(context);
        }
        catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
        }
        return context;
    }

1. 开启计时器

开启计时器,用来统计应用启动的时间

public void start() throws IllegalStateException {
		// 传入一个空字符串作为当前任务的名称
        this.start("");
    }

    public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
        	// 如果当前任务名字不为空,抛出异常
            throw new IllegalStateException("Can't start StopWatch: it's already running");
        } else {
        	// 否则,记录当前任务的开始时间
            this.currentTaskName = taskName;
            this.startTimeNanos = System.nanoTime();
        }
    }

2. 监听器

private SpringApplicationRunListeners getRunListeners(String[] args) {
		// 类加载对应的监听器
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 创建SpringApplicationRunListener实例
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

getSpringFactoriesInstances同样又是去读取META-INF/spring.factories然后拿到key为SpringApplicationRunListener的集合,并实例化

SpringApplicationRunListener作用就是用来发布事件或者说运行监听器

这里拿到的就是EventPublishingRunListener

 然后执行starting方法,发布ApplicationStartingEvent事件

	@Override
	public void starting(ConfigurableBootstrapContext bootstrapContext) {
		this.initialMulticaster
				.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
	}
getApplicationListeners拿到所有监听ApplicationStartingEvent的监听器

 之后会调用监听器,最后执行监听器的onApplicationEvent(event)方法

 其实就是告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。

3.初始化默认参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

根据命令行参数 实例化一个ApplicationArguments

4.预初始化环境

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment
        // 根据webApplicationType 创建Environment  创建就会读取: java环境变量和系统环境变量
		ConfigurableEnvironment environment = getOrCreateEnvironment();
         // 将命令行参数读取环境变量中
		configureEnvironment(environment, applicationArguments.getSourceArgs());
         // 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
		ConfigurationPropertySources.attach(environment);
        // 发布了ApplicationEnvironmentPreparedEvent 的监听器  读取了全局配置文件
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
        // 将所有spring.main 开头的配置信息绑定SpringApplication
		bindToSpringApplication(environment);
        // 如果不是web应用环境,将环境转换成StandardEnvironment
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
        //更新PropertySources
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

listeners.environmentPrepared(bootstrapContext, environment);

发布了ApplicationEnvironmentPreparedEvent 的事件

	@Override
	public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(
				new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
	}

告诉SpringApplicationRunListener的监听器:“当前SpringBoot应用使用的Environment准备好了咯!”。

 其中有一个EnvironmentPostProcessorApplicationListener

onApplicationEnvironmentPreparedEvent方法中读取了全局配置文件(application.yml)

 我看的源码是2.5.4版本的,之前2.3.6版本的应该是ConfigFileApplicationListener

5.创建Spring应用上下文

protected ConfigurableApplicationContext createApplicationContext() {
	// 首先进行判断有没有指定的实现类
	Class<?> contextClass = this.applicationContextClass;
	// 如果没有,则根据应用类型选择
	if (contextClass == null) {
		try {
			// 根据webApplicationType的类型去反射创建ConfigurableApplicationContext的具体实例
			switch (this.webApplicationType) {
			case SERVLET:
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
		}
	}
	// 通过反射,得到创建的对象
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

对于Web应用,上下文类型是DEFAULT_WEB_CONTEXT_CLASS。

6.预初始化spring上下文

	private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
        // 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
		applyInitializers(context);
        // 发布了ApplicationContextInitializedEvent
		listeners.contextPrepared(context);
		bootstrapContext.close(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
        // 获取当前spring上下文beanFactory (负责创建bean)
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
        // 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
       // 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
        // 设置当前spring容器是不是要将所有的bean设置为懒加载
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
        // 读取主启动类 (因为后续要根据配置类解析配置的所有bean)
		load(context, sources.toArray(new Object[0]));
        //4.读取完配置类后发送ApplicationPreparedEvent。
		listeners.contextLoaded(context);
	}

7.Spring上下文刷新

加载spring ioc 容器    **相当重要

由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器

所以springboot对它做了扩展:

加载自动配置类:invokeBeanFactoryPostProcessors 方法

创建servlet容器:onRefresh方法

private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			shutdownHook.registerApplicationContext(context);
		}
		refresh(context);
	}

 最终会走到spring加载ioc的refresh()方法,看过spring源码的肯定知道这个方法

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				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();
			}
		}
	}

8.Spring上下文后置处理

在Spring容器刷新上下文后进行调用,依次调用注册的Runners。

/**
 * Called after the context has been refreshed.
 * @param context the application context
 * @param args the application arguments
 */
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

private void callRunners(ApplicationContext context, ApplicationArguments args) {
	List<Object> runners = new ArrayList<>();
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
	AnnotationAwareOrderComparator.sort(runners);
	// CommandLineRunner、ApplicationRunner 这两个接口,是在容器启动成功后的最后一步进行回调
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

9. 停止计时器

做计时监听器停止操作,并统计一些任务执行信息

public void stop() throws IllegalStateException {
    if (this.currentTaskName == null) {
        throw new IllegalStateException("Can't stop StopWatch: it's not running");
    } else {
        long lastTime = System.nanoTime() - this.startTimeNanos;
        this.totalTimeNanos += lastTime;
        this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
        if (this.keepTaskList) {
            this.taskList.add(this.lastTaskInfo);
        }

        ++this.taskCount;
        this.currentTaskName = null;
    }
}


10. 发布Spring上下文启动完成事件

void started(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.started(context);
    }
}


11. 执行所有 Runner 运行器

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}


12. 发布Spring上下文就绪事件

void running(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.running(context);
    }
}

触发所有 SpringApplicationRunListener 监听器的 running 事件的方法。
 

总结:

1. 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。

2.运行run方法

3.读取 环境变量 配置信息.....

4. 创建springApplication上下文:ServletWebServerApplicationContext

5. 预初始化上下文 : 读取启动类

6.调用refresh 加载ioc容器

加载所有的自动配置类

创建servlet容器

7.在这个过程中springboot会调用很多监听器对外进行

SpringBoot 事件监听器发布顺序:(官网也有)

1.ApplicationStartingEvent在运行开始时发送,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送。

2.在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent。

3.准备ApplicationContext并调用ApplicationContextInitializers之后,将发送ApplicationContextInitializedEvent。

4.读取完配置类后发送ApplicationPreparedEvent。

5.在刷新上下文之后但在调用任何应用程序和命令行运行程序之前,将发送ApplicationStartedEvent。

6.紧随其后发送带有LivenessState.CORRECT的AvailabilityChangeEvent,以指示该应用程序被视为处于活动状态。

7.在调用任何应用程序和命令行运行程序之后,将发送ApplicationReadyEvent。

8.紧随其后发送ReadabilityState.ACCEPTING_TRAFFIC的AvailabilityChangeEvent,以指示应用程序已准备就绪,可以处理请求。

9.如果启动时发生异常,则发送ApplicationFailedEvent。

参考文章:

SpringBoot启动原理及相关流程

深入剖析Springboot启动原理的底层源码,再也不怕面试官问了!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值