1.SpringApplication初始化
1.1 SpringApplication静态方法run方法
我们找一个SpringBoot应用,然后打开应用启动主类,我们知道主应用的启动会调用SpringApplication的静态方法#run方法。run方法的执行会伴随调用SpringApplication的构造器。我点进去瞧一瞧:
@SpringBootApplication(exclude={SecurityAutoConfiguration.class})
public class SpringBootApp{
public static void main(String[] args){
SpringApplication.run(SpringBootApp.class, args);
}
}
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
//此构造器是SpringApplication初始化的核心方法。
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//(1)推断web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//(2)加载Spring应用上下文初始化器this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//(3)加载Spring应用事件监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//(4)推断应用引导类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
}
在执行构造器过程种依次调用(1)(2)(3)(4)处的方法,下面就逐一分析每个方法的调用过程。
1.1.1 推断web应用类型
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
//检查当前ClassLoader下是否存在否写类作为判断标准
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
}
通过ClassUtils.isPresent方法依次推断DispatcherHandler、DispatcherServlet、ServletContainer、ConfigurableWebApplicationContext的存在性组合情况,从而判断web应用类型。
1.1.2 加载Spring应用上下文初始化器
首选看到这个步骤分两步走,
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
先看第一步
- this.getSpringFactoriesInstances()
我仔细看看这个方法做了什么,
public class SpringApplication {
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
//加载对象,
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建对象,就是循环names,然后创建对象,并把对象放在list集合中
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//将对象进行排序
AnnotationAwareOrderComparator.sort(instances);
//返回的是对象数组
return instances;
}
}
SpringFactoriesLoader#loadFactoryNames()每当看到loader都要引起注意。此处运用了Spring工厂加载机制。看到这里心中有些疑问:从哪里加载的对象?加载那些对象?我们跟进去瞧一瞧:
源码如下,粗略扫一眼有点似曾相识的感觉,就好像见到了前任一样,感觉好像在哪见过。没错,如果看过上一篇文章的小伙伴,想必已经能回忆起来了,这不就是加载META-INF/spring.factories资源配置文件下指定的实现类嘛,看看this.getSpringFactoriesInstances(ApplicationContextInitializer.class)者个方法传的参数,换言之就是要加载ApplicationContextInitializer接口类的实现类对象数组。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, 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 factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
不相信我们就去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.web.context.ServerPortInfoApplicationContextInitializer
小伙伴们,这次明白了吧,SpringBoot自动装配就是这个niaoxing。
看完第一步在看第二步做了什么
- this.setInitializers()
//就是把上一步的对象放在initializers中
private List<ApplicationContextInitializer<?>> initializers;
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList();
this.initializers.addAll(initializers);
}
1.1.3 加载Spring应用事件监听器
好吧,这里看完了,看明白前面加载Spring应用上下文初始化器可以pass掉了,因为这一步和上面一样。
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
1.1.4 推断应用引导类
找到main方法启动入口类
private Class<?> deduceMainApplicationClass() {
try {
//获取当前线程执行栈数组
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
//判断那个方法包含main方法。
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
;//第一次见源码这么写
}
return null;
}
至此SpringApplication初始化过程已经分析完了,小伙伴们,是不是感觉不过瘾,那我们继续深入深入,这个过程会很爽的。
2. SpringApplication运行阶段
2.1 SpringApplication核心方法run方法
运行时调用SpringApplication#run方法,现在我们一起来看看它的真容。
//SpringApplication.class
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//(1)获取SpringApplicationRunListeners应用运行时监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
接下来我们还是逐一分析每个方法。
2.1.1 获取SpringApplicationRunListeners应用运行时监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}