SpringBoot启动流程深入解析源码(一)---SpringApplication构造函数

Spring Boot作为目前主流JavaWeb框架具有如下优点:

  • 快速创建独立运行的Spring项目以及与主流框架集成
  • 使用嵌入式Servlet容器,应用不需要打包成war
  • starts自动依赖与版本控制
  • 无需大量配置,简化开发
  • 准生产化境的运行时应用监控
  • 与云计算集成

而Spring Boot内部是如何实现的这些优点的呢,我们一步一步揭开Spring Boot的面纱。

准备一个Spring Boot项目

现在搭建一个Spring Boot项目是如此的简单快捷,不会经历以前Spring的配置炼狱。
Cc同学使用开发工具是IDEA,新建项目,选择Spring Initializr,一路默认,勾选组件楼主勾选了Spring Web,为了方便演示Web环境的启动。
完成创建,稍等片刻,我们就得到了一个Spring Boot项目。我这里使用的Spring Boot版本是2.6.1
在这里插入图片描述

进入调试阶段

在这里插入图片描述
打上断点直接Debug运行,步入run方法内部。
在这里插入图片描述
这里我建议 点一下 Download Sources,这样IDEA会帮我们下载好源码,更加方便调试。
在这里插入图片描述
可以看到这里实际上是先创建SpringApplication的实例,然后调用run方法,这篇文章我们先讲解SpringApplication的构造函数中做了什么工作。
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));
	//推断当前应用类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	this.bootstrapRegistryInitializers = new ArrayList<>(
			getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
	//设置初始化器
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	//设置监听器
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	//推断主类
	this.mainApplicationClass = deduceMainApplicationClass();
}

deduceFromClasspath()方法内部就是根据Classpath下是否存在提前定义好的这些类来推断当前应用类型,WebApplicationType一共有三种属性 NONE,SERVLET,REACTIVE。

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)) {
		return WebApplicationType.REACTIVE;
	}
	for (String className : SERVLET_INDICATOR_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
	//默认是SERVLERT
	return WebApplicationType.SERVLET;
}

getSpringFactoriesInstances()
这里要着重讲一下getSpringFactoriesInstances()方法,在设置初始化器和监听器时都用到了这个方法,而我们的监听器和初始化器都是从哪来的我们就要详细看一下这个方法,后续这个方法用到的地方比较多

//参数是一个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
	//官方这里的注释是使用全类名并确保唯一,以防止重复
	//这里主要是获取参数传进来的Class类型对应的全类名
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	//通过反射获得上面类的实例
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	//根据order接口排序
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

跟进一下 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法;

//SpringFactoriesLoader内的静态变量
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}
	//获取传进来的Class的全类名
	//示例: org.springframework.context.ApplicationListener
	String factoryTypeName = factoryType.getName();
	//可以看到下方的loadSpringFactories()返回值是一个Map,这里通过factoryTypeName在Map里寻找,如果没有则返回空集合,如果可以找到的话返回一个List<String>的集合
	return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	//这里通过类加载器类型去缓存中获取,这里的cache是一开始上方定义的是一个Map类型
	//key值就是类加载器,value是一个Map<String, List<String>>
	Map<String, List<String>> result = cache.get(classLoader);
	if (result != null) {
		//如果缓存不为null则直接返回
		return result;
	}
	result = new HashMap<>();
	try {
		//获取"META-INF/spring.factories"的资源位置
		Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		while (urls.hasMoreElements()) {
			//这里以我电脑得到的url做个示例:
			//jar:file:/C:/Users/lemon/.m2/repository/org/springframework/boot/spring-boot/2.6.1/spring-boot-2.6.1.jar!/META-INF/spring.factories
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			//获取Properties对象解析内容
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryTypeName = ((String) entry.getKey()).trim();
				String[] factoryImplementationNames =
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
				for (String factoryImplementationName : factoryImplementationNames) {
					result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
							.add(factoryImplementationName.trim());
				}
			}
		}
		// Replace all lists with unmodifiable lists containing unique elements
		result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
				.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
		//将结果放入缓存中,下次直接查询缓存
		cache.put(classLoader, result);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
	return result;
}

spring.factories的内容示例:
在这里插入图片描述
cache示例:(其实存放的就是spring.factories的内容)
在这里插入图片描述这时我们在看这行代码
loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName,Collections.emptyList());
实际就是在Map里获取对应的配置类名。
最后一步,推断主类。

private Class<?> deduceMainApplicationClass() {
	try {
		//获取方法调用栈
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			//找到main方法
			//equals非常好的一个习惯就是使用字符串调用equals而非使用变量,这样会避免空指针异常
			if ("main".equals(stackTraceElement.getMethodName())) {
				//返回main方法所在的Class
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

至此,SpringBoot启动流程的第一节,SpringApplication构造函数分析就到这里了,接下来就是真正的run方法,有不理解或讲错的,同学可以下方留言一起探讨进步。

第二章链接:
SpringBoot启动流程深入解析源码(二)—SpringApplicationRunListeners

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值