springboot原始碼解析-管中窺豹系列之web伺服器(七)
阿新 • • 發佈:2021-02-04
# 一、前言
- Springboot原始碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
- 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot原始碼管中窺豹系列。
![ 簡介 ](https://zhangbin1989.gitee.io/blog/picture/zb0018_springsour/springboot_source_0.png)
# 二、web伺服器
- 以前的的spring專案或者springmvc專案都需要一個web伺服器,tomcat,或者其它的
- 使用springboot之後,我們不再需要配置web伺服器,因為springboot幫我們集成了
- 今天我們來分析一下原始碼,看看在哪裡實現的,知其然知其所以然
# 三、原始碼分析
- 還是從SpringApplication的run方法開始看
- 不熟悉的可以看之前的文章:springboot原始碼解析-管中窺豹系列之總體結構(一)
```
SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
...
try {
...
refreshContext(context);
...
}
catch (Throwable ex) {
...
}
...
return context;
}
```
- 接著進入到 refreshContext(context) 裡面
```
AbstractApplicationContext.java
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
try {
...
// Initialize other special beans in specific context subclasses.
onRefresh();
...
}
catch (BeansException ex) {
...
}
finally {
...
}
}
}
```
- 進入到 onRefresh() 方法
```
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
```
- 注意這個是一個protected方法,我們進入到子實現裡面
- 具體用的哪個context,請看之前的文章:springboot原始碼解析-管中窺豹系列之專案型別(二)
```
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);
}
```
- 最常用的就是普通web專案,我們看這一個
- 我們到org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext裡面找 onRefresh() 方法
- 沒找到,在父類ServletWebServerApplicationContext裡面找到了
```
ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
```
- 我們到 createWebServer() 方法裡面
```
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
```
- 用的工廠模式,先找到工廠getWebServerFactory()
- 再用工廠生成webServer, factory.getWebServer(getSelfInitializer())
- 先看看 getWebServerFactory() 這個方法
```
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory()
.getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : "
+ StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
```
- 獲取唯一的工廠:ServletWebServerFactory,多了少了都不行
- 在哪載入進springboot的呢?
```
```
- spring-boot-starter-web裡面是包含了spring-boot-starter依賴的
- spring-boot-starter裡面包含了spring-boot-autoconfigure依賴
- spring-boot-autoconfigure裡面有一個類:ServletWebServerFactoryConfiguration
- 這個類裡面有一個靜態類: EmbeddedTomcat
```
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
Object