SpringApplication的构造方法源码分析:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 传入的资源加载器为null,在执行run方法中对其赋值默认值
// org.springframework.boot.SpringApplication#run(java.lang.String...)
// Banner printedBanner = printBanner(environment);
// ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader());
// org.springframework.util.ClassUtils#getDefaultClassLoader
// Thread.currentThread().getContextClassLoader()
this.resourceLoader = resourceLoader;
// 主类不能为空(传入空数组new Class<?>[0]可以跳过此验证)
Assert.notNull(primarySources, "PrimarySources must not be null");
// 可以有多个主类进入,但是只有当前启动的main方法的类是后面的mainApplication,其他的做为资源类加入BeanFactory中管理
// private Set<Class<?>> primarySources;
// 关联参考:org.springframework.boot.SpringApplication#getAllSources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 通过反射加载相关应用类,来推断web应用的类型
// WebApplicationType.REACTIVE
// WebApplicationType.NONE
// WebApplicationType.SERVLET 当前项目默认启动就是servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 自动装配:加载配置于/META-INF/spring.factories中key为org.springframework.context.ApplicationContextInitializer的所有初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 同上操作,加载监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 通过栈推断main方法所在的启动类,在spring cloud 中的bootstrap启动中会利用此主类
// 参考org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext
// StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
this.mainApplicationClass = deduceMainApplicationClass();
}
1.自动装配 setInitializers
// \org\springframework\boot\spring-boot\2.2.4.RELEASE\spring-boot-2.2.4.RELEASE.jar!\META-INF\spring.factories
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
// \org\springframework\boot\spring-boot-autoconfigure\2.2.4.RELEASE\spring-boot-autoconfigure-2.2.4.RELEASE.jar!\META-INF\spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
源码分析setInitializers:
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
a.获取所有的初始化器实例对象:
// org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// parameterTypes = new Class<?>[] {} 为空
// args 为空
// 获取classLoader
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 根据传入的类和classLoader加载META-INF/spring.factories中的配置
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建配置实例对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
b.获取META-INF/spring.factories文件指定key的配置:
// org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 从所有配置中获取指定的配置
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
c.加载META-INF/spring.factories文件的所有配置:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先看看缓存中有没有,有就返回,springboot第一次肯定没有,但是spring cloud当使用bootstrap启动时,bootstrap会创建SpringApplication子容器,加缓存可以提高效率
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// classLoader != null
// FACTORIES_RESOURCE_LOCATION = META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
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());
}
}
}
// 以类加载器为key,将加载的自动装配的配置文件放入map缓存中
// private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
d.反射加载实例对象:
// org.springframework.boot.SpringApplication#createSpringFactoriesInstances
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
// parameterTypes = new Class<?>[] {}
// args 为空
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
// 反射加载初始化器的类信息
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 验证父类的实例是否可以从子类得到分配
// superType.isAssignableFrom(subType)
Assert.isAssignable(type, instanceClass);
// 反射获取构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 反射创建实例java.lang.reflect.Constructor#newInstance
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
2.自动装配 setListeners
观察者模式:org.springframework.context.ApplicationListener
// \org\springframework\boot\spring-boot\2.2.4.RELEASE\spring-boot-2.2.4.RELEASE.jar!\META-INF\spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
// \org\springframework\boot\spring-boot-autoconfigure\2.2.4.RELEASE\spring-boot-autoconfigure-2.2.4.RELEASE.jar!\META-INF\spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
源码和初始化器一致
3.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;
}
SpringApplication的构造方法总结
- 赋值加载器,加载器为null,后续使用默认加载器
- 验证主配置类不能为空
- 推断当前项目的web类型
- 自动装配初始化器
- 自动装配监听器
- 通过栈推断main方法所在的启动类