背景
1> 大家都知道SpringBoot是通過main函式啟動的,這里面跟蹤代碼到處都沒有找到while(true),為什么啟動后可以一直跑?
2> SpringBoot默認使用tomcat作為web容器,大家也可以通過在pom檔案中exclusion掉tomcat,denpendency jetty 的方法來使用jetty,那SpringBoot是怎么做到在不同web容器之間切換的呢?
3> 傳統的web容器比如jetty本質上是直接通過java start.jar 來啟動,之后來加載spring背景關系的,SpringBoot通過main函式是怎么來啟動web容器的呢?
本文就這三個問題展開論述,
問題1分析
問題1很簡單,啟動后一直跑是因為啟動了執行緒池,原理就是有非deamon的執行緒在跑,Java虛擬機規范定義要等所有用戶執行緒都運行完才會退出,
所以這個原理就和下面啟動執行緒池一樣

程式員修煉之道教我們:不要假定,要證明,雖然jetty使用執行緒池是常識,我們也來跟蹤下原始碼,看看執行緒池是在哪里初始化的:
org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory類里,創建Server的使用使用執行緒池作為初始化引數,然后創建了socket連接來監聽埠,(對于socket連接有之前沒接觸過的,可以自己查一下,建議動手實踐,《Java例外處理總結》這篇文章里有不錯的簡單小例子可以實操下,)

到這里,大家應該都明白了為什么啟動后一直不停,但是又有疑問了:JettyServletWebServerFactory是個什么東東?
問題2分析
關于問題2,我們寫個最簡單的類來debug一下:

進入SpringAppication.run的原始碼可以看到,里面創建了一個context,默認是AnnotationConfigServletWebServerApplicationContext,一初始化,在Bean定義里就加載了spring開天辟地的5個Bean,

繼續向下執行走到AbstractApplicationContext的refresh方法,執行到onRefresh時,你進入方法里發現實際上執行的是
ServletWebServerApplicationContext的onFresh

這里面實際只做了一件事:創建web服務,

進入這個方法,debug到getWebServerFactory

來看一下:

獲取的正式JettyServletWebServerFactory,為啥不是TomcatServlet呢?ServletWebServerFactoryAutoConfiguration的原始碼很好的說明了這個問題,原始碼的大意是當tomcat依賴存在就用tomcat,不然就按順序找jetty存不存在,不存在再找Undertow存不存在,找到了就回傳這個bean作為Servlet的工廠類,
@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
public ServletWebServerFactoryAutoConfiguration() {
}
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(
name = {"org.apache.catalina.startup.Tomcat"}
)
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public BeanPostProcessorsRegistrar() {
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
}
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory != null) {
this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
}
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
}
至此第二個問題也真相大白,
問題3分析
第三個問題是傳統的web容器比如jetty本質上是直接通過java start.jar 來啟動,之后來加載spring背景關系的,SpringBoot通過main函式是怎么來啟動web容器,
這個問題在前面問題分析程序中也給了很多線索,我們來回顧下:SpringApplication.run里會創建Spring的應用背景關系,默認是AnnotationConfigServletWebServerApplicationContext,首先會加載Spring開天辟地的5個Bean,然后它初始化各種Bean工廠,
SpringBoot在ServletWebServerApplicationContext中多載了onRefresh方法,除了以前Spring默認的onRefresh方法外還增加了createWebServer方法,在這個方法中對Web容器進行了初始化作業,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
</exclusion>
</exclusions>
</dependency>
因為選擇servlet容器是類似于使用基于條件的注解方式,因為當exclusion掉tomcat后,只有jetty滿足條件,所以會加載JettyServletWebServerFactory,
通過getWebServer方法會new一個WebServer物件,new物件的方法會呼叫initialize方法,在這個方法中會對容器進行初始化并啟動,
而容器啟動的基本原理就是創建個執行緒池和網路套接字,用執行緒去處理套接字讀寫的內容,
總結
文本用帶有少許說明的三個問題開場展開論述,實際是使用了麥肯錫大法中的SCQA架構,
SCQA架構是金字塔模型里面突出的一個論述方法,即“情境(Situation)、沖突(Complication)、問題(Question)、答案(Answer)”,可以幫助我們在陳述事實時條理更為清晰、有效,
SCQA其實只是麥肯錫做了總結,這個方法李清照都在用:
昨夜雨疏風驟,濃睡不消殘酒 (情境)
試問卷簾人,卻道海棠依舊(沖突)
知否,知否(問題)
應是綠肥紅瘦(答案)
文章正文看似一步步回答問題,實際上在講述怎樣去看spring原始碼,了解spring原理的一個程序,即:帶著問題去看,debug跟蹤原始碼驗證 的方法,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/211007.html
標籤:其他
上一篇:物件與this
下一篇:技術點8:jsp頁面
