SpringBoot(七)启动流程分析之SpringApplication对象创建

SpringBoot 专栏收录该内容
27 篇文章 5 订阅

SpringBoot版本:2.1.1      ==》启动流程分析汇总

能力有限,没解释到或解释不正确的地方忘谅解,欢迎评论指正,持续更新。

目录

准备工作

流程分析

1、SpringApplication的构造方法

1.1、推断应用程序类型

1.2、设置Initializers

1.2、设置Listener

1.3、推断main方法所在类


准备工作

 新建子项目SpringBoot_StartupPprocess,导入的依赖是spring-boot-starter-web,为web环境。

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.eastcom</groupId>
		<artifactId>SpringBoot_Demo</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<artifactId>SpringBoot_StartupPprocess</artifactId>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
</project>

创建主类Application。main方法的两种编写方式如下。直接走第二种方式的流程。

@SpringBootApplication
public class Application {
	public void main(String[] args) {
		//第一种方式:直接调用SpringApplication的静态run方法(内部转换后用下面第二种方法)
		SpringApplication.run(Application.class, args);
		
		//第二种方式:先实例化SpringApplication,再调用run方法
		SpringApplication application = new SpringApplication(Application.class);
		application.run(args);
	}
}

流程分析

1、SpringApplication的构造方法

来看一下在SpringApplication对象的构造方法中都做了哪些事。

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

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
        //判断primarySources不能为空
	Assert.notNull(primarySources, "PrimarySources must not be null");
        //将primarySources放入SpringApplication的全局变量primarySources,Set集合中
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //从类路径中推断应用程序类型放到SpringApplication的全局变量webApplicationType 
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //从META-INF/spring.factories文件中获取ApplicationContextInitializer接口的实现类并利用反射创建对象返回放入SpringApplication的全局变量initializers,List集合中
	setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        //同上,也是从META-INF/spring.factories文件中获取ApplicationListener接口的实现类并利用反射创建对象返回放入SpringApplication的全局变量listeners,List集合中
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //通过获取当前调用栈,找到入口方法main所在的类,放入SpringApplication的全局变量mainApplicationClass 
	this.mainApplicationClass = deduceMainApplicationClass();
}

1.1、推断应用程序类型

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

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

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

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


static WebApplicationType deduceFromClasspath() {
        //ClassUtils.isPresent()从默认classloader中判断是否存在对应的类型
	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;
}

推断逻辑是:

先是判断默认的classloader中是否存在org.springframework.web.reactive.DispatcherHandler、且不存在org.springframework.web.servlet.DispatcherServlet、org.glassfish.jersey.servlet.ServletContainer,如果为true返回WebApplicationType.REACTIVE

然后循环String数组,判断如果默认的classloader中是否不存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,如果不存在,则认为不是web应用程序,返回WebApplicationType.NONE

最后是返回WebApplicationType.SERVLET

三种返回类型的解释如下:

        1、WebApplicationType.NONE:不是web应用程序

        2、WebApplicationType.SERVLET:基于servlet的Web应用程序运行

        3、WebApplicationType.REACTIVE:响应式的web应用程序

 

1.2、设置Initializers

传入参数是class类型即ApplicationContextInitializer.class,最终调用方法是getSpringFactoriesInstances,注意第二个参数,传的是一个空的Class数组,至于为什么要注意这个参数,马上就解释。

SpringFactoriesLoader.loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader):这个方法,其作用是使用给定的类加载器从META-INF/spring.factories加载给定类型的工厂实现的完全限定类名,即得到所有ApplicationContextInitializer接口实现类的完全限定类名,去重。

setInitializers((Collection) 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
        //得到所有ApplicationContextInitializer接口的实现类
	Set<String> names = new LinkedHashSet<>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //创建所有ApplicationContextInitializer接口实现类的实例
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
        //根据order排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

随后当做参数传递到createSpringFactoriesInstances方法中,这个方法主要作用就是根据传入的type类型,parameterTypes参数类型(空Class数组)以及得到的完全限定类名集合创建对象实例,其中getDeclaredConstructor方法作用是得到指定参数类型的构造方法,parameterTypes为空数组即的得到的就是默认构造方法。构造方法基本都是空的,所以无需关心创建Initializers实例的时候在构造方法中执行了什么操作。这些对象的initialize方法会在后面的run方法中被调用。

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
		Set<String> names) {
        //new 一个跟检索出来的接口实现类相同size的List
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
                        //通过类加载器加载类
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                        //判断是否为ApplicationContextInitializer接口的实现类
			Assert.isAssignable(type, instanceClass);
                        //得到指定参数类型的构造方法
			Constructor<?> constructor = instanceClass
					.getDeclaredConstructor(parameterTypes);
                        //创建对象
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
                        //放到List中,返回
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
					"Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

 spring.factories文件内容中Initializers如下。

 

 

1.2、设置Listener

这一步跟上面设置Initializers执行的操作是一样的。spring.factories文件内容中Listener如下。

整理一下Listener对应的Event,有不对的地方望评论指正。

 

BackgroundPreinitializerApplicationStartingEvent、

ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、

ApplicationReadyEvent、ApplicationFailedEvent、ApplicationContextInitializedEvent
ClearCachesApplicationListenerContextRefreshedEvent
ParentContextCloserApplicationListenerParentContextAvailableEvent
FileEncodingApplicationListenerApplicationEnvironmentPreparedEvent
AnsiOutputApplicationListenerApplicationEnvironmentPreparedEvent
ConfigFileApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent
DelegatingApplicationListenerApplicationEnvironmentPreparedEvent
ClasspathLoggingApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationFailedEvent
LoggingApplicationListenerApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ContextClosedEvent、ApplicationFailedEvent
LiquibaseServiceLocatorApplicationListenerApplicationStartingEvent

1.3、推断main方法所在类

StackTraceElement数组包含了StackTrace(堆栈轨迹)的内容,通过遍历它可以得到当前方法以及其定义类、调用行数等信息。


private Class<?> deduceMainApplicationClass() {
	try {
                //获取StackTraceElement数组,也就是这个栈的信息。
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			if ("main".equals(stackTraceElement.getMethodName())) {
                                //stackTraceElement.getClassName(),得到定义类,即main方法所在类
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

到这里,SpringApplication对象的创建过程就完成了。

 

  • 2
    点赞
  • 2
    评论
  • 6
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页

打赏

天黑黑~

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值