SpringBoot啓動原理

背景java

1> 你們都知道SpringBoot是經過main函數啓動的,這裏面跟蹤代碼處處都沒有找到while(true),爲何啓動後能夠一直跑?程序員

2> SpringBoot默認使用tomcat做爲web容器。你們也能夠經過在pom文件中exclusion掉tomcat,denpendency jetty 的方法來使用jetty。那SpringBoot是怎麼作到在不一樣web容器之間切換的呢?web

3> 傳統的web容器好比jetty本質上是直接經過java start.jar 來啓動,以後來加載spring上下文的,SpringBoot經過main函數是怎麼來啓動web容器的呢?spring

本文就這三個問題展開論述。apache

 

問題1分析tomcat

問題1很簡單,啓動後一直跑是由於啓動了線程池。原理就是有非deamon的線程在跑。Java虛擬機規範定義要等全部用戶線程都運行完纔會退出。網絡

因此這個原理就和下面啓動線程池同樣架構

程序員修煉之道教咱們:不要假定,要證實。雖然jetty使用線程池是常識,咱們也來跟蹤下源碼,看看線程池是在哪裏初始化的:eclipse

 

org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory類裏,建立Server的使用使用線程池做爲初始化參數。而後建立了socket鏈接來監聽端口。(對於socket鏈接有以前沒接觸過的,能夠本身查一下。建議動手實踐。《Java異常處理總結》這篇文章裏有不錯的簡單小例子能夠實操下。)socket

 

到這裏,你們應該都明白了爲何啓動後一直不停。可是又有疑問了: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跟蹤源碼驗證 的方法。

相關文章
相關標籤/搜索