前言
springBoot是一个基于spring开发的一个快速开发框架,用来简化Spring项目的初始搭建和开发的过程。使用其特定的方式简化复杂的xml配置工作,通俗的讲就是“约定优于配置”,这篇博文将分析一下SpringApplication的构造方法。源码版本:SpringBoot-2.2.11-RESEALE
程序入口
在spring项目中,需要创建启动类,调用SpringApplication.run方法启动项目,所以以此处作为分析入口。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
构造方法
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
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));
// 推导出当前启动的main方法所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
获取web应用程序类型
方法内通过判断webflux相关的类是否存在,存在这是RESCTIVE类型,不存在继续判断servlet是否存在,存在则是SERVLET,不存在则是NONE。
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() {
// ClassUtils.isPresent 通过这个方法来判断常量中的类是否存在
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
// 响应式web项目,对应 spring-webflux
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
// 非web项目,不需要创建web容器
return WebApplicationType.NONE;
}
}
// servlet项目,对应spring mvc项目
return WebApplicationType.SERVLET;
}
设置应用上下文初始化器和设置应用监听器
这两步用到都是同一个SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>)方法,只是传入的不同的类型。
getSpringFactoriesInstances
一般是用来解析META-INF/spring.factories文件中部分接口的实现类,实现可拔插的效果。
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();
// 从META-INF/spring.factories文件中加载和实例化给定类型的工厂。
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据names反射创建对应的实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
使用给定的类加载器从“META-INFspring.factories”加载给定类型的工厂实现的完全限定类名。
SpringFactoriesLoader这个类是spring的,不是boot专有的,源码在org.springframework.core.io.support中。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 获取spring.factories中key对应的接口名字
String factoryTypeName = factoryType.getName();
// 将spring.factories配置转为map后,通过接口全名获取对应的配置项list
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
/**
* 将spring.factories中的键值对转换为map对象
* @param classLoader
* @return
*/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
通过反射创建对应的实例
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
// 通过反射创建实例
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 根据参数适配对应的构造方法
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
推导出当前启动的main方法所在的类
/**
* 推导出当前启动的main方法所在的类
* @return
*/
private Class<?> deduceMainApplicationClass() {
try {
// 新建一个RuntimeException异常,获取当前堆栈信息
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;
}