創建SpringApplication對象 SpringBoot版本為 2.1.1.RELEASE @SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] arg ...
創建SpringApplication對象
SpringBoot版本為 2.1.1.RELEASE
@SpringBootApplication
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
這是一個SpringBoot項目的啟動類,在主方法內,調用了SpringApplication類的靜態run( )方法,並且把啟動類的位元組碼和主方法的參數作為函數入參。
我們以Debug方式來運行代碼,可以發現SpringBoot項目在啟動時會先創建SpringApplication對象。具體代碼如下:
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);
}
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
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();
}
可以發現,其實在創建SpringApplcation對象時就是把啟動類的Class類作為入參調用了SpringApplication類的構造方法。
下麵將會重點說明一下這個構造方法:
A: this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
這行代碼是將啟動類存儲到SpringApplicationd對象的primarySources 屬性中
B: this.webApplicationType = WebApplicationType.deduceFromClasspath();
這個方法是推斷Web應用類型
static WebApplicationType deduceFromClasspath() {
//存在DispatcherHander但是不存在DispatcherServlet時,推斷為WebFlux類型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 當Servlet和ConfigurableWebApplicationContext均不存在時,推斷為非Web應用
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
由於我們只引入了web依賴,所以返回為Servlet
C: setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
這個方法是用來載入Spring應用上下文初始化器的。
我們debug進入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();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
其實這個方法就是通過SpringFactoriesLoader類的loadFactoryNames()方法從類路徑下的 "META-INF/spring.factories" 文件中找到所有的 "ApplicationContextInitializer",然後保存到 SpringApplication的initializers屬性中。
在下圖中,我們可以看到通過getSpringFactoriesInstances()方法,initializers屬性中保存了6個初始化器
D: setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
和上個方法類似,只不過這個方法是載入Spring應用事件監聽器,但是原理是 一樣的,都是利用SpringFactoriesLoader類的loadFactoryNames()方法,只不過這個初始化的對象變成了ApplicationListener
E: this.mainApplicationClass = deduceMainApplicationClass();
這個方法的作用是從多個配置類中找到含有main方法的主配置類
其實這個方法設計的並不嚴謹,因為在SpringApplication.run(XXX.class)方法中,這個class對象並一定要傳入其主配置類,因為我們知道@SpringBootApplication 元標註 @EnableAutoConfiguration,它們的作用基本相同,換言之,這裡的class 對象我們可以傳入任意標註了@EnableAutoConfiguration、@Configuration兩個註解的類。
下一篇文章將會講解SpringApplicaiton中run()方法的運行。