springboot启动加载流程

我们都知道,我们平时启动springboot项目都是通过在main方法中运行SpringApplication.run来启动的,今天我们通过源码去详细了解一下springboot的启动加载流程

开始看代码之前,我们回顾一下我们平时在使用springboot时的一些问题,然后带着问题去看代码。
1、springboot的默认配置文件application.properties是如何加载的
2、springboot是如何进行bean加载的
3、springboot在启动过程中还做了哪些动作

STEP1 程序启动入口
    public static void main(String[] args) {
    SpringApplication.run(ACTestPlatform.class, args);
}

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

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

以上是run方法的调用链路,我们可以发现它最终调用了SpringApplication的构造方法实例化,然后执行run方法。接下来我们去具体看一下它在实例化和执行run方法时具体做了哪些事情

STEP2 实例化SpringApplication对象
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		#推断web应用类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		#加载工厂实例
		setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
		#加载监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		#推断启动类
		this.mainApplicationClass = deduceMainApplicationClass();
}

我们发现,SpringApplication的构造方法做的主要事情就是为一些重要属性赋值。接下来我们逐个分析这些属性是如何赋值的

1、WebApplicationType.deduceFromClasspath() 推断web应用类型
	static WebApplicationType deduceFromClasspath() {
	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
			&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
		return WebApplicationType.REACTIVE;
	}
	for (String className : SERVLET_INDICATOR_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
	return WebApplicationType.SERVLET;
}

这段代码中会去判断一些定义的类名常量在项目中是否存在,从而判断属于不同的web应用。具体的判断逻辑为:

  • 如果存在reactive包且不存在servlet包,则为REACTIVE类型
  • 如果不存在servlet包,则为NONE类型
  • 否则为SERVLET类型
2、getSpringFactoriesInstances(ApplicationContextInitializer.class) 加载spring工厂实例
	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<String> names = new LinkedHashSet<>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
3、实例化步骤
  • 通过SpringFactoriesLoader.loadFactoryNames方法去加载所有META-INF/spring.factories文件中工厂实例,存放在MultiValueMap中,key和value是一对多关系,key是接口名,value是实现类名。
  • 在map中查找ApplicationContextInitializer对应的值。
  • 通过createSpringFactoriesInstances方法去创建所有初始化器的实例
  • 对实例进行排序
4、getSpringFactoriesInstances(ApplicationListener.class) 加载所有监听器

和加载工厂实例一致,不再赘述

5、mainApplicationClass = deduceMainApplicationClass() 推断启动类
	private Class<?> deduceMainApplicationClass() {
	try {
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

调用main方法所在类作为启动类

STEP3 调用run方法
public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	//该方法会通过SpringFactoriesLoader.loadFactoryNames方法去获取SpringApplicationRunListener对应的资源,并实例化。我们发现只有一个实现类EventPublishingRunListener,实例化过程中会为其initialMulticaster属性赋值,将step2中加载到的所有监听器存放到对象initialMulticaster的applicationListeners属性中
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

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

run方法中的方法调用比较多,我们这里重点关注几个关键的点

1、通过监听器事件发布初始化
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();	

这段代码首先通过getRunListeners方法去在加载的META-INF/spring.factories中配置的资源,获取接口名为SpringApplicationRunListener的接口实现类,我们发现只有一个实现EventPublishingRunListener。
springboot在实例化该实现类时,在构造方法中为它的initialMulticaster属性赋值,initialMulticaster属性是一个对象,类型为SimpleApplicationEventMulticaster,值存放在该对象的defaultRetriever(对象,类型为ListenerRetriever)的applicationListeners属性中。
此处会将前面加载到SpringApplication的listeners属性中的所有监听器遍历赋值给applicationListeners。

	public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}

调用listeners.starting()方法时,会去遍历上一步赋值的applicationListeners属性,为所有监听器发布ApplicationStartingEvent事件,做一些初始化动作

	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		Executor executor = getTaskExecutor();
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}
2、加载配置
ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);

通过prepareEnvironment方法加载配置

private ConfigurableEnvironment prepareEnvironment(
		SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// Create and configure the environment
	//创建ConfigurableEnvironment对象,通过不同的webApplicationType类型选择不
	同的子类实例化。实例化过程中会加载系统配置信息和环境变量(System.getProperties、System.getenv)
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	
	//配置environment,设置转换器,添加active profile等
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	
	//发布ApplicationEnvironmentPreparedEvent事件。在ConfigFileApplicationListener
	这个监听器中,会加载默认配置文件,文件名可以为application.xml|properties|yml|yaml,
	根据不同文件类型调用不同的解析器去解析
	listeners.environmentPrepared(environment);
	//这个方法会去将配置文件中spring.main对应的配置绑定的SpringApplication这个类的相关属性上去。具体做法为通过反射去获取当前类及所有祖先类(到Object为止)中所有get、is、set开头的方法,将符合条件的方法和属性组装成一个BeanProperty对象。然后去遍历之前加载的所有配置资源中的所有配置,将以spring.main开头的属性值拿到,然后将经过转换器做了类型转换的值通过反射赋给SpringApplication的属性
	bindToSpringApplication(environment);
	//环境转换
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader())
				.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值