自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我們一起學(xué)學(xué)嵌入式Web容器

開發(fā) 架構(gòu)
Spring Boot 支持的兩大 Web 容器體系,一個是 Servlet Web ,另一個是 Reactive Web ,它們都有其具體的容器實現(xiàn),相信大多數(shù)開發(fā)者使用的都是前者,且最常用的容器實現(xiàn)也是 Tomcat,所以這篇文章主要討論的也是 Spring Boot 啟動 Tomcat 嵌入式容器的流程。

前言

開始之前呢,我們帶著幾個問題去學(xué)習(xí):

1、Spring Boot 嵌入式Web容器是什么?

2、整體流程或結(jié)構(gòu)是怎樣的?

3、核心部分是什么?

4、怎么實現(xiàn)的?

1、起源

在當(dāng)今的互聯(lián)網(wǎng)場景中,與終端用戶交互的應(yīng)用大多數(shù)是 Web 應(yīng)用,其中 Java Web 應(yīng)用尤為突出,其對應(yīng)的 Java Web 容器發(fā)展至今也分為 Servlet Web 容器和 Reactive Web 容器,前者的使用率大概占比是百分之九十左右,其具體的實現(xiàn)有 Tomcat、Jetty 和 Undertow;而后者出現(xiàn)較晚,且技術(shù)棧體系并未完全成熟,還有待時間驗證可行性,它的默認(rèn)實現(xiàn)為 Netty Web Server。其中的 Servlet 規(guī)范與三種 Servlet 容器的版本關(guān)系如下:

Servlet 規(guī)范

Tomcat

Jetty

Undertow

4

9.X

9.X

2.X

3.1

8.X

8.X

1.X

3

7.X

8.X

N/A

2.5

6.X

8.X

N/A

以上 Web 容器均被 Spring Boot 嵌入至其中作為其核心特性,來簡化 Spring Boot 應(yīng)用啟動流程。Spring Boot 通過 Maven 依賴來切換應(yīng)用的嵌入式容器類型,其對應(yīng)的 Maven jar 分別是:

<!-- Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>

<!-- undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

<!-- jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

<!-- netty Web Server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</dependency>

前三者是 Servlet Web 實現(xiàn),最后則是 Reactive Web 的實現(xiàn)。值得注意的是,當(dāng)我們引用的是 Servlet Web 功能模塊時,它會自動集成 Tomcat ,里面包含了 Tomcat 的 Maven 依賴包,也就是說 Tomcat 是默認(rèn)的 Servlet Web 容器。Servlet Web 模塊的 Maven 依賴如下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

而如果引用的是 Reactive Web 功能模塊時,則會默認(rèn)集成 netty Web Server 。Reactive Web 模塊的依賴如下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

不過,上面的三種 Servlet Web 容器也能作為 Reactive Web 容器 ,并允許替換默認(rèn)實現(xiàn) Netty Web Server,因為 Servlet 3.1+容器同樣滿足 Reactive 異步非阻塞特性。

接下來,我們重點討論嵌入式 Web 容器的啟動流程。

注:本篇文章所用到的 Spring Boot版本是 2.3.3.RELEASE

2、容器啟動流程解析

Spring Boot 嵌入式容器啟動時會先判斷當(dāng)前的應(yīng)用類型,是 Servlet Web 還是 Reactive Web ,之后會創(chuàng)建相應(yīng)類型的 ApplicationContext 上下文,在該上下文中先獲取容器的工廠類,然后利用該工廠類創(chuàng)建具體的容器。接下來,我們進行詳細(xì)討論。

從 Spring Boot 啟動類開始:

@SpringBootApplication
public class DiveInSpringBootApplication {
public static void main(String[] args){
SpringApplication.run(DiveInSpringBootApplication.class, args);
}
}

2.1獲取應(yīng)用類型

先來看看,獲取應(yīng)用類型的過程,進入 run 的重載方法:

public class SpringApplication {

// 1、該方法中,先構(gòu)造 SpringApplication 對象,再調(diào)用該對象的 run 方法。我們進入第二步查看構(gòu)造過程
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args){
return new SpringApplication(primarySources).run(args);
}

// 2、webApplicationType 存儲的就是應(yīng)用的類型,通過 deduceFromClasspath 方法返回。// 我們進入第三步查看該方法實現(xiàn)public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){
...
this.webApplicationType = WebApplicationType.deduceFromClasspath();
...
}
}

public enum WebApplicationType {

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";

// 3、這里其實是根據(jù)引入的 Web 模塊 jar 包,來判斷是否包含各 Web 模塊的類,來返回相應(yīng)的應(yīng)用類型
static WebApplicationType deduceFromClasspath(){

// 當(dāng) DispatcherHandler 類存在,DispatcherServlet 和 ServletContainer 不存在時,
// 返回 Reactive ,表示當(dāng)前 Spring Boot 應(yīng)用類型是 Reactive Web 。
// 前者是 Reactice Web jar 中的類,后兩者是 Servlet Web 中的。if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}

// 這里判斷是非 Web 應(yīng)用類型for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}

// 以上都不滿足時,最后返回 Servlet 。return WebApplicationType.SERVLET;
}
}

以上,在 SpringApplication 的構(gòu)造階段確定了當(dāng)前應(yīng)用的類型,該類型名稱存儲在 webApplicationType 字段中。

2.2容器啟動流程

接著進入容器啟動流程,進入重載的 run 方法中:

public class SpringApplication {

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_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";

...

private WebApplicationType webApplicationType;

...

public ConfigurableApplicationContext run(String... args){
...

ConfigurableApplicationContext context = null;

try {
...
// 1、通過 createApplicationContext 方法創(chuàng)建對應(yīng)的 ApplicationContext 應(yīng)用上下文,進入 1.1 查看具體實現(xiàn)
context = createApplicationContext();

...

// 2、該方法實質(zhì)是啟動 Spring 應(yīng)用上下文的,但 Spring Boot 嵌入式容器也在該過程中被啟動,入?yún)⑹巧舷挛膶ο?,我們進入 2.1 進行跟蹤
refreshContext(context);

...
}
...
}

// 1.1、protected ConfigurableApplicationContext createApplicationContext(){
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {

// 這里就是通過 webApplicationType 屬性,判斷應(yīng)用類型,來創(chuàng)建不同的 ApplicationContext 應(yīng)用上下文switch (this.webApplicationType) {
case SERVLET:

// 返回的是 Servlet Web ,具體對象為 AnnotationConfigServletWebServerApplicationContext,// 該類有一個關(guān)鍵父類 ServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:

// 返回的是 Reactive Web,具體對象為 AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:

// 應(yīng)用類型是非 Web 時,返回 AnnotationConfigApplicationContext
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
...
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

// 2.1private void refreshContext(ConfigurableApplicationContext context){

// 里面調(diào)用的是 refresh 方法,進入 2.2 繼續(xù)跟蹤
refresh(context);

...
}

// 2.2protected void refresh(ApplicationContext applicationContext){
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);

// 最終調(diào)用了 所有應(yīng)用上下文的統(tǒng)一抽象類 AbstractApplicationContext 中的 refresh 方法,進入 3 查看實現(xiàn)
((AbstractApplicationContext) applicationContext).refresh();
}

// ...
}

AbstractApplicationContext 是 Spring 應(yīng)用上下文的核心啟動類,Spring 的 ioc 從這里就開始進入創(chuàng)建流程。在后續(xù)在 Spring 系列的文章中會進行詳細(xì)討論,我們這里只關(guān)注容器創(chuàng)建的部分。

public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {

...

// 3
@Overridepublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {

try {
...

// Web 容器在這個方法中啟動,但在當(dāng)前抽象類中這個方法是個空實現(xiàn),具體由 ApplicationContext 上下文的子類對象進行重寫,
// 我們進入 4 查看其中一個子類的實現(xiàn)
onRefresh();

...
}
}
...
}
...
}

這里以 Servlet web 為例,具體上下文對象是
AnnotationConfigServletWebServerApplicationContext,該類實現(xiàn)了 ServletWebServerApplicationContext 類,onRefresh 方法由該類進行重寫。

public class ServletWebServerApplicationContext extends GenericWebApplicationContextimplements ConfigurableWebServerApplicationContext {
...

// 4
@Overrideprotected void onRefresh(){

...

try {

// 里面調(diào)用了 createWebServer 方法,進入 5 查看實現(xiàn)
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

...

// 5private void createWebServer(){

// WebServer 就是容器對象,是一個接口,其對應(yīng)的實現(xiàn)類分別是:
// JettyWebServer、TomcatWebServer、UndertowWebServer、NettyWebServer。這些容器對象由對應(yīng)的容器工廠類進行創(chuàng)建
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();

// 當(dāng) webServer 等于 null ,也就是容器還沒創(chuàng)建時,進入該 if 中if (webServer == null && servletContext == null) {

// 這里是獲取 創(chuàng)建 Servlet Web 容器的工廠類,也是一個接口,有三個實現(xiàn),分別是:
// JettyServletWebServerFactory、TomcatServletWebServerFactory、UndertowServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();

// 通過容器工廠類的 getWebServer 方法,創(chuàng)建容器對象。這里以創(chuàng)建 Tomcat 為例,來繼續(xù)跟蹤容器的創(chuàng)建流程,// 跳到 6 查看 TomcatServletWebServerFactory 的 getWebServer 方法this.webServer = factory.getWebServer(getSelfInitializer());
}

...
}
}

TomcatServletWebServerFactory 是創(chuàng)建 Tomcat 的 Web 容器工廠類,但這個工廠類是如何被創(chuàng)建的呢?這里將會在文章的第三部分進行詳細(xì)討論。

public class TomcatServletWebServerFactory extends AbstractServletWebServerFactoryimplements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {

...
// 6
@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers){

// 方法中先進行創(chuàng)建 Tomcat 的流程,如 Container 、Engine、Host、Servlet 幾個容器的組裝。// 后續(xù)有機會再對 Tomcat 進行詳細(xì)討論,這里就不深入了
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);

// 通過 getTomcatWebServer 方法返回 TomcatWebServer 容器對象。進入 7 查看接下來的流程return getTomcatWebServer(tomcat);
}

...

// 7、這里通過 TomcatWebServer 的構(gòu)造方法創(chuàng)建該對象。進入 8 繼續(xù)跟蹤protected TomcatWebServer getTomcatWebServer(Tomcat tomcat){
return new TomcatWebServer(tomcat, getPort() >= 0);
}

...
}

TomcatWebServer 是具體的容器對象,在其對應(yīng)的工廠類中進行創(chuàng)建,其實現(xiàn)了 WebServer 接口,并在該對象中進行 Tomcat 的啟動流程。

public class TomcatWebServer implements WebServer {

...

// 8、
public TomcatWebServer(Tomcat tomcat, boolean autoStart){
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;

// 進入 initialize 方法中,查看實現(xiàn)
initialize();
}

private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {

...

// 調(diào)用 Tomcat 的 start 方法,正式啟動this.tomcat.start();

...

// 啟動守護線程來監(jiān)聽http請求
startDaemonAwaitThread();
}
...
}
}

...
}

到這里,Web 容器的啟動流程就結(jié)束了,以上是以 Servlet Web 及其具體的 Tomcat 容器為例子進行的討論,這也是大部分開發(fā)者常用的體系。接著來對整個過程做一個總結(jié)。

首先通過引入的 Web 模塊 Maven 依賴 ,來判斷當(dāng)前應(yīng)用的類型,如 Servlet Web 或 Reactive Web。并根據(jù)應(yīng)用類型來創(chuàng)建相應(yīng)的 ApplicationContext 上下文對象。然后調(diào)用了 ApplicationContext 的父抽象類
AbstractApplicationContext 中的 refresh 方法,又在該方法中調(diào)用了子類的 onRefresh 方法。最后是在 onRefresh 中通過容器的工廠類創(chuàng)建具體容器對象,并在該容器對象中進行啟動。

3、加載 Web 容器工廠

上面說過, Web 容器對象是由其對應(yīng)的工廠類進行創(chuàng)建的,那容器工廠類又是怎么創(chuàng)建呢?我們這里就來看一看。在《Spring Boot 自動裝配(二)》的 1.2 小節(jié)說過, Spring Boot 啟動時,會讀取所有 jar 包中 META-INF 文件夾下的 spring.factories 文件,并加載文件中定義好的類,如:

...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\\

...

其中有一個
ServletWebServerFactoryAutoConfiguration 類:

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
...
}

該類通過 @Import 導(dǎo)入了
ServletWebServerFactoryConfiguration 中的三個內(nèi)部類,這三個內(nèi)部類就是用來創(chuàng)建容器工廠,我們進入其中查看具體實現(xiàn):

@Configuration
class ServletWebServerFactoryConfiguration {

// 通過 @ConditionalOnClass 判斷 Servlet 、Tomcat、UpgradeProtocol 這三個 Class 是否存在,
// 當(dāng)引用的是 Tomcat Maven 依賴時,則 Class 才存在,并創(chuàng)建 Tomcat 的容器工廠類 TomcatServletWebServerFactory@Configuration@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
}

// 當(dāng)引用的是 Jetty Maven 依賴時,@ConditionalOnClass 條件才滿足,
// 創(chuàng)建的容器工廠類是 JettyServletWebServerFactory@Configuration@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {

@Beanpublic JettyServletWebServerFactory JettyServletWebServerFactory(){
return new JettyServletWebServerFactory();
}
}

// 當(dāng)引用的是 Undertow Maven 依賴時,@ConditionalOnClass 條件才滿足,
// 創(chuàng)建的容器工廠類是 UndertowServletWebServerFactory@Configuration@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {

@Beanpublic UndertowServletWebServerFactory undertowServletWebServerFactory(){
return new UndertowServletWebServerFactory();
}
}

可以看到,主要是通過引入相應(yīng) Web 容器的 Maven 依賴,來判斷容器對應(yīng)的 Class 是否存在,存在則創(chuàng)建相應(yīng)的容器工廠類。

4、總結(jié)

最后,來對 Spring Boot 嵌入式 Web 容器做一個整體的總結(jié)。Spring Boot 支持的兩大 Web 容器體系,一個是 Servlet Web ,另一個是 Reactive Web ,它們都有其具體的容器實現(xiàn),相信大多數(shù)開發(fā)者使用的都是前者,且最常用的容器實現(xiàn)也是 Tomcat,所以這篇文章主要討論的也是 Spring Boot 啟動 Tomcat 嵌入式容器的流程。

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2022-04-07 09:29:04

文件系統(tǒng)硬盤操作系統(tǒng)

2022-07-28 08:52:08

Docker命令操作系統(tǒng)

2021-12-14 09:34:31

丑數(shù)順序指針

2024-07-26 09:47:28

2022-01-10 06:52:59

拖拽庫項目搜索

2024-01-30 09:14:35

容器資源管理

2023-06-27 13:47:00

分布式事務(wù)本地事務(wù)

2023-07-04 08:06:40

數(shù)據(jù)庫容器公有云

2024-04-29 13:07:00

運維保命原則Oracle

2023-11-06 08:28:43

2023-02-09 08:35:39

Spring嵌入式容器

2023-11-07 08:13:53

分布式網(wǎng)絡(luò)

2022-06-28 08:16:35

MySQL數(shù)據(jù)容災(zāi)

2023-08-04 08:20:56

DockerfileDocker工具

2021-01-12 05:08:49

DHCP協(xié)議模型

2021-08-27 07:06:09

DubboDocker技術(shù)

2022-03-31 18:59:43

數(shù)據(jù)庫InnoDBMySQL

2022-05-24 08:21:16

數(shù)據(jù)安全API

2023-08-10 08:28:46

網(wǎng)絡(luò)編程通信

2022-10-18 07:33:57

Maven構(gòu)建工具
點贊
收藏

51CTO技術(shù)棧公眾號