前言:本次源码分析使用SpringBoot-2.2.5.RELEASE版本。
SpringBoot启动入口
想要启动一个SpringBoot应用很简单,只需要在包的根目录下创建一个类,并且在类上添加注解@SpringBootApplication标注其是一个SpringBoot应用即可:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
通过执行该启动类的Main方法即可启动一个SpringBoot应用,接下来整个SpringBoot的启动过程就是通过SpringApplication的run方法来进行。
SpringApplication初始化
SpringApplication类可用于从main方法引导和启动Spring应用程序,默认情况下该类将执行以下步骤来引导程序的启动:
- 创建一个ApplicationContext上下文实例
- 注册CommandLinePropertySource以将命令行参数公开为Spring属性
- 刷新应用程序上下文并加载所有单例Bean
该类的初始化由该类调用自身的run方法开始,再调用自身重载的run方法后得到初始化机会:
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);
}
上面的new 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();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
核心的初始化分为以下三个步骤:
初始化WebApplicationType
应用类型的初始化通过WebApplicationType.deduceFromClasspath()来进行,点击进入其中查看对应源码:
public enum 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;
}
}
return WebApplicationType.SERVLET;
}
}
可以看出,通过反射判断环境是否存在这个类来判断获取初始化的web应用类型。
初始化Initializers
用于读取所有META-INF/spring.factories文件中配置了ApplicationContextInitializer的初始化器:
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
可以看出它将从spring.factories文件中获取的配置进行实例化后,封装成一个集合对SpringApplication中的initializers集合属性进行初始化。
初始化Listeners
跟初始化Initializers类似也是通过读取所有META-INF/spring.factories文件中配置的ApplicationListener类型的监听器,最终配置被实例化之后封装成一个集合对SpringApplication中的listeners集合属性进行初始化:
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
值得注意的是,不管是初始化Initializers或者初始化Listeners,上面都省略了一个步骤,即读取配置和实例化的步骤,是因为它是一个通用读取的过程,以下详细讲解。
要读取配置中的组件信息,需要调用通用方式getSpringFactoriesInstances并传入对应的参数类型,例如:
# 读取ApplicationContextInitializer类型的配置信息
getSpringFactoriesInstances(ApplicationContextInitializer.class)
# 读取ApplicationListener类型的配置信息
getSpringFactoriesInstances(ApplicationListener.class)
这里列举了ApplicationContextInitializer类型和ApplicationListener类型在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
# 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
点击getSpringFactoriesInstances方法进入内部查看源码:
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();
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
可以看出最终它是由loadFactoryNames方法解析出配置并存放到Set集合中,最终将该集合作为参数传入createSpringFactoriesInstances方法,通过Set参数集合实例化出一个个实例。
而这里的Set集合就是一个个的ApplicationContextInitializer或ApplicationListener所对应的组件信息,点击loadFactoryNames方法进入,需要注意的是参数type为要读取的配置类型:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
通过以上的loadSpringFactories方法,它遍历了每个jar包中的META-INF/spring.factories文件,并把spring.factories文件中的每个配置全部读取出来放到一个map中,每个map的key就是spring.factories文件中对应的一个属性配置key,value则是这个key所对应的一些需要初始化的组件,如下图展示其中一个spring.factories文件内容,以及读取出来的组件信息,以ApplicationContextInitializer类型为例:
# 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
该配置对应读取出来的map信息为:
最后在loadFactoryNames方法中通过factoryTypeName即ApplicationContextInitializer这个类型来筛选出其中的实现类,得到了以下结果:
这就是ApplicationContextInitializer类型的Set<String>集合,拿到该集合后需要将集合中的ApplicationContextInitializer类型的实现进行一个个实例化出来,所以调用createSpringFactoriesInstances方法并将上述解析出来的实现集合作为参数传入(另外还有一个重要的参数即SpringApplication,这个后续绑定监听器时有用):
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;
}
这段代码的意思就是将把spring.factories文件中的每个配置全部读取出来放到一个map中后,遍历该map的key,通过反射的方式实例化出来放到放instances集合中。
至此,整个SpringApplication实例初始化完成,而核心的属性Initializers和Listeners会在后续启动做作为重要参数来初始化。
SpringApplicationRunListeners初始化
待续...
网友评论