springboot源碼解析-管中窺豹系列之項目類型(二)
- 2021 年 1 月 8 日
- 筆記
- springboot, 源碼
一、前言
- Springboot源碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
- 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot源碼管中窺豹系列。
二、項目類型
這一節我們先討論一下springboot項目的怎麼自動載入applicationcontext實現類的。
- 以前的spring的項目,都是xml載入bean,常用的都是XmlWebApplicationContext實現類
- 後來出現註解的形式,基本用AnnotationConfigWebApplicationContext實現類
- 後來又出現響應式編程reactive
那springboot用的是哪一種呢?
三、源碼解讀
先說結論:關於類型的選擇,springboot是根據class來判斷的。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
this.webApplicationType = WebApplicationType.deduceFromClasspath();
...
}
- 我們從SpringApplication的構造函數開始看起
- 構造函數裡面有段程式碼:是確定類型的
我們先看類型:
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";
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;
}
- 第一段if,如果存在reactive包的DispatcherServlet,同時不存在jersey和mvc的DispatcherHandler,就是REACTIVE
- 第二段循環,不存在Servlet或者ConfigurableWebApplicationContext,就是none
- 剩下的就是我們熟悉的SERVLET
類型確定之後,我們看SpringApplication的run方法:
public ConfigurableApplicationContext run(String... args) {
...
try {
...
context = createApplicationContext();
...
} catch (Throwable ex) {
...
}
...
return context;
}
ApplicationContext實現類就是在createApplicationContext()裡面確定的
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
根據類型載入不同的class:
- 如果是SERVLET:AnnotationConfigServletWebServerApplicationContext
- 如果是REACTIVE:
AnnotationConfigReactiveWebServerApplicationContext - default:AnnotationConfigApplicationContext
至此,一目了然了,想要不同的項目類型,添加對應的jar包,springboot自動幫你選擇對應的ApplicationContext實現類
如果是普通的web項目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如果是reactive項目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
如果是非web項目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
歡迎關注公眾號:豐極,更多技術學習分享。