SpringApplication原始碼剖析(一)——構造方法
SpringApplication原始碼剖析(一)——構造方法
之前通過看了AbstractApplicationContext.class、DefaultListableBeanFactory.class、AbstractBeanFactory.class幾個類基本瞭解到了SpringIoC相關特性的原理具體程式碼實現。從這篇文章開始將打算了解以下SpringBoot應用的啟動過程。SpringBoot應用入口是SpringApplication.run()方法,因此從這個方法為入口,看一下相關的原始碼。
構造方法
在閱讀run()方法的原始碼時,發現有些類屬性是在構造方法中初始化的,因此先寫一篇關於構造方法的筆記,記錄以下幾個後面會用到的比較特殊的屬性。
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 = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this .applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
this.sources = new LinkedHashSet();
spring應用的源source?
this.bannerMode = Mode.CONSOLE;
banner的模式,banner是指SpringBoot啟動的時候列印的:
一共三種列舉:LOG、CONSOLE、OFF
this.addCommandLineProperties = true;
若addCommandLineProperties設定為true,則如果啟動時命令列傳入的引數不為空,則把引數以SimpleCommandLinePropertySource物件例項形式新增到environment.getPropertySources()中
相關程式碼:
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
if (this.addCommandLineProperties && args.length > 0) {
String name = "commandLineArgs";
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
} else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
this.addConversionService = true;
相關程式碼主要有兩處:
1、設定環境變數時,若addConversionService為true,則把sharedInstance注入到ConfigurableEnvironment中
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
this.configurePropertySources(environment, args);
this.configureProfiles(environment, args);
}
2、若addConversionService為true,則把sharedInstance注入到beanFactory中
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
......
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
this.registerShutdownHook = true;
若設定為true,則起一條執行緒監聽當前執行緒發生shutdown時呼叫AbstractApplicationContext.doClose()方法。
相關程式碼:
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
this.refresh((ApplicationContext)context);
}
public void registerShutdownHook() {
if (this.shutdownHook == null) {
this.shutdownHook = new Thread("SpringContextShutdownHook") {
public void run() {
synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
AbstractApplicationContext.this.doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
this.lazyInitialization = false;
設定bean定義是否預設為懶載入,若設定為true,則會新增LazyInitializationBeanFactoryPostProcessor到context的beanFactoryPostProcessor列表中,遍歷所有BeanDefinition,若沒有設定lazyInit屬性,則預設修改為true。
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
預設的applicationContext工廠,根據webApplicationType返回對應的context。
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch(webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
} catch (Exception var2) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
}
};
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
SpringApplication建構函式傳入的class引數,儲存在primarySource屬性中
this.webApplicationType = WebApplicationType.deduceFromClasspath();
根據不同的webApplicationType生成對應的ApplicationContext、Enviroment屬性。判斷條件:
1、若存在DispatcherHandler,則webApplicationType=REACTIVE
2、若存在"javax.servlet.Servlet"、"org.springframework.web.context.ConfigurableWebApplicationContext"都有載入,則返回SERVLET,(SERVLET_INDICATOR_CLASSES = new String[]{“javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext”};)
3、否則返回NONE
相關判斷程式碼:
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;
}
}
this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));
獲取所有Bootstrapper.class型別bean,在BoostrapperContext初始化階段會呼叫所有的Boostrapper的initialize方法初始化context:
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> {
initializer.intitialize(bootstrapContext);
});
return bootstrapContext;
}
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
獲取所有ApplicationContextInitializer.class型別bean,在ApplicationContext初始化階段會呼叫所有initialzer初始化context,發生在BeanDefinition載入之前。
this.mainApplicationClass = this.deduceMainApplicationClass();
根據呼叫棧找到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];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}