您現在的位置是:首頁 > 藝術

「Spring boot」整合tomcat底層原理

由 綻放蒼穹的程式設計師 發表于 藝術2023-01-11
簡介 orderedStream()呼叫時會去Spring容器中找到TomcatConnectorCustomizer型別的Bean,預設是沒有的,程式設計師可以自己定義

server物件有什麼作用

本文轉載自【微信公眾號:程式JAVA圈】經微信公眾號授權轉載,如需轉載請與原文作者聯絡。

「Spring boot」整合tomcat底層原理

本文結論

原始碼基於spring boot2。6。6

專案的pom。xml中存在spring-boot-starter-web的時候,在專案啟動時候就會自動啟動一個Tomcat。

自動配置類ServletWebServerFactoryAutoConfiguration找到系統中的所有web容器。我們以tomcat為主。

構建TomcatServletWebServerFactory的bean。

SpringBoot的啟動過程中,會呼叫核心的refresh方法,內部會執行onRefresh()方法,onRefresh()方法是一個模板方法,他會執行會執行子類ServletWebServerApplicationContext的onRefresh()方法。

onRefresh()方法中呼叫getWebServer啟動web容器。

spring-boot-starter-web內部有什麼?

在spring-boot-starter-web這個starter中,其實內部間接的引入了spring-boot-starter-tomcat這個starter,這個spring-boot-starter-tomcat又引入了tomcat-embed-core依賴,所以只要我們專案中依賴了spring-boot-starter-web就相當於依賴了Tomcat。

「Spring boot」整合tomcat底層原理

內部的starter-tomcat。png

「Spring boot」整合tomcat底層原理

內部的tomcat-embed-core。png

自動配置類:ServletWebServerFactoryAutoConfiguration

在spring-boot-autoconfigure-2。6。6。jar這個包中的spring。factories檔案內,配置了大量的自動配置類,其中就包括自動配置tomcat的自動配置類:ServletWebServerFactoryAutoConfiguration

「Spring boot」整合tomcat底層原理

自動配置類的配置。png

自動配置類的程式碼如下

// full模式

@Configuration

proxyBeanMethods

=

false

// 配置類解析順序

@AutoConfigureOrder

Ordered

HIGHEST_PRECEDENCE

// 條件註解:表示專案依賴中要有ServletRequest類(server api)

@ConditionalOnClass

ServletRequest

class

// 表示專案應用型別得是SpringMVC(在啟動過程中獲取的SpringBoot應用型別)

@ConditionalOnWebApplication

type

=

Type

SERVLET

// 讀取server下的配置檔案

@EnableConfigurationProperties

ServerProperties

class

// import具體的載入配置的類和具體web實現容器

@Import

({

ServletWebServerFactoryAutoConfiguration

BeanPostProcessorsRegistrar

class

ServletWebServerFactoryConfiguration

EmbeddedTomcat

class

ServletWebServerFactoryConfiguration

EmbeddedJetty

class

ServletWebServerFactoryConfiguration

EmbeddedUndertow

class

})

public

class

ServletWebServerFactoryAutoConfiguration

{

……

}

ServletRequest是存在於tomcat-embed-core-9。0。60。jar中的的一個類,所以@ConditionalOnClass(ServletRequest。clas s)會滿足。

spring-boot-starter-web中,間接的引入了spring-web、spring-webmvc等依賴,所以@ConditionalOnWebApplication(type = Type。SERVLET)條件滿足。

上面的倆個條件都滿足,所以spring回去解析這個配置類,在解析過程中會發現他import了三個類!我們重點關注

EmbeddedTomcat

。其他倆個的內部條件註解不滿足!

@Configuration

proxyBeanMethods

=

false

// tomcat內部的類,肯定都存在

@ConditionalOnClass

({

Servlet

class

Tomcat

class

UpgradeProtocol

class

})

// 程式設計師如果自定義了ServletWebServerFactory的Bean,那麼這個Bean就不載入。

@ConditionalOnMissingBean

value

=

ServletWebServerFactory

class

search

=

SearchStrategy

CURRENT

static

class

EmbeddedTomcat

{

@Bean

TomcatServletWebServerFactory

tomcatServletWebServerFactory

ObjectProvider

<

TomcatConnectorCustomizer

>

connectorCustomizers

ObjectProvider

<

TomcatContextCustomizer

>

contextCustomizers

ObjectProvider

<

TomcatProtocolHandlerCustomizer

<?>>

protocolHandlerCustomizers

{

TomcatServletWebServerFactory

factory

=

new

TomcatServletWebServerFactory

();

// orderedStream()呼叫時會去Spring容器中找到TomcatConnectorCustomizer型別的Bean,預設是沒有的,程式設計師可以自己定義。這個Bean可以設定一些tomcat的配置,比如埠、協議。。。

// TomcatConnectorCustomizer:是用來配置Tomcat中的Connector元件的

factory

getTomcatConnectorCustomizers

()。

addAll

connectorCustomizers

orderedStream

()。

collect

Collectors

toList

()));

// TomcatContextCustomizer:是用來配置Tomcat中的Context元件的

factory

getTomcatContextCustomizers

()。

addAll

contextCustomizers

orderedStream

()。

collect

Collectors

toList

()));

// TomcatProtocolHandlerCustomizer:是用來配置Tomcat中的ProtocolHandler元件的

factory

getTomcatProtocolHandlerCustomizers

()。

addAll

protocolHandlerCustomizers

orderedStream

()。

collect

Collectors

toList

()));

return

factory

}

}

}

對於另外的EmbeddedJetty和EmbeddedUndertow,邏輯類似,都是判斷專案依賴中是否有Jetty和Undertow的依賴,如果有,那麼對應在Spring容器中就會存在JettyServletWebServerFactory型別的Bean、或者存在UndertowServletWebServerFactory型別的Bean。

TomcatServletWebServerFactory的作用:獲取WebServer物件

TomcatServletWebServerFactory他實現了ServletWebServerFactory這個介面。

ServletWebServerFactory介面內部只有一個方法是獲取WebServer物件。

「Spring boot」整合tomcat底層原理

ServletWebServerFactory介面。png

WebServer擁有啟動、停止、獲取埠等方法,就會發現WebServer其實指的就是Tomcat、Jetty、Undertow。

「Spring boot」整合tomcat底層原理

WebServer介面。png

而TomcatServletWebServerFactory就是用來生成Tomcat所對應的WebServer物件,具體一點就是TomcatWebServer物件,並且在生成TomcatWebServer物件時會把Tomcat給啟動起來。

在原始碼中,呼叫TomcatServletWebServerFactory物件的getWebServer()方法時就會啟動Tomcat。

public

WebServer

getWebServer

ServletContextInitializer

。。。

initializers

{

if

this

disableMBeanRegistry

{

Registry

disableRegistry

();

}

// 構建tomcat物件

Tomcat

tomcat

=

new

Tomcat

();

// 設定相關的屬性

File

baseDir

=

this

baseDirectory

!=

null

this

baseDirectory

createTempDir

“tomcat”

);

tomcat

setBaseDir

baseDir

getAbsolutePath

());

for

LifecycleListener

listener

this

serverLifecycleListeners

{

tomcat

getServer

()。

addLifecycleListener

listener

);

}

Connector

connector

=

new

Connector

this

protocol

);

connector

setThrowOnFailure

true

);

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

);

// 啟動tomcat,這個方法內部有this。tomcat。start();

return

getTomcatWebServer

tomcat

);

}

spring boot啟動的時候啟動tomcat

SpringBoot的啟動過程中,會呼叫核心的refresh方法,內部會執行onRefresh()方法,onRefresh()方法是一個模板方法,他會執行會執行子類ServletWebServerApplicationContext的onRefresh()方法。

protected

void

onRefresh

()

{

// 模板方法,先呼叫它父類的,一般是空方法

super

onRefresh

();

try

{

// 建立web容器

createWebServer

();

}

catch

Throwable

ex

{

throw

new

ApplicationContextException

“Unable to start web server”

ex

);

}

}

這個方法會呼叫createWebServer()方法。

// 最核心的倆行程式碼

private

void

createWebServer

()

{

……

// 獲取web容器,多個或者沒有的時候報錯

ServletWebServerFactory

factory

=

getWebServerFactory

();

// 呼叫這個容器的getWebServer方法,上面的啟動tomcat的方法!

this

webServer

=

factory

getWebServer

getSelfInitializer

());

……

}

getWebServerFactory控制專案組有且只能有一個web容器!

protected

ServletWebServerFactory

getWebServerFactory

()

{

// Use bean names so that we don‘t consider the hierarchy

// 得到所有型別為ServletWebServerFactory的Bean。TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory都是他得到子類!

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

));

}

// 返回唯一的一個web容器!

return

getBeanFactory

()。

getBean

beanNames

0

],

ServletWebServerFactory

class

);

}

獲取tomcat的配置

自動配置類ServletWebServerFactoryAutoConfiguration上除了import三個web容器,還import了BeanPostProcessorsRegistrar。

BeanPostProcessorsRegistrar實現了ImportBeanDefinitionRegistrar,所以他會在spring啟動的時候呼叫registerBeanDefinitions方法。

registerBeanDefinitions會註冊一個Bean:webServerFactoryCustomizerBeanPostProcessor。

public

void

registerBeanDefinitions

AnnotationMetadata

importingClassMetadata

BeanDefinitionRegistry

registry

{

// Bean工廠,一個Aware回撥進行賦值

if

this

beanFactory

==

null

{

return

}

// 註冊webServerFactoryCustomizerBeanPostProcessor這個Bean。

registerSyntheticBeanIfMissing

registry

“webServerFactoryCustomizerBeanPostProcessor”

WebServerFactoryCustomizerBeanPostProcessor

class

WebServerFactoryCustomizerBeanPostProcessor

::

new

);

// 註冊errorPageRegistrarBeanPostProcessor

registerSyntheticBeanIfMissing

registry

“errorPageRegistrarBeanPostProcessor”

ErrorPageRegistrarBeanPostProcessor

class

ErrorPageRegistrarBeanPostProcessor

::

new

);

}

webServerFactoryCustomizerBeanPostProcessor實現了BeanPostProcessor,所以他會在啟動的時候呼叫postProcessBeforeInitialization方法。

private

void

postProcessBeforeInitialization

WebServerFactory

webServerFactory

{

// 找到WebServerFactoryCustomizer的Bean

LambdaSafe

callbacks

WebServerFactoryCustomizer

class

getCustomizers

(),

webServerFactory

// 標記日誌用的類

withLogger

WebServerFactoryCustomizerBeanPostProcessor

class

// 呼叫customize方法,傳入webServerFactory

invoke

((

customizer

->

customizer

customize

webServerFactory

));

}

postProcessBeforeInitialization中會呼叫WebServerFactoryCustomizer類customize方法,在系統中的唯一實現:ServletWebServerFactoryCustomizer的customize方法。

customize把配置中的內容設定到ConfigurableServletWebServerFactory物件中。他的實現TomcatServletWebServerFactory在啟動的時候就會有值!

@Override

public

void

customize

ConfigurableServletWebServerFactory

factory

{

PropertyMapper

map

=

PropertyMapper

get

()。

alwaysApplyingWhenNonNull

();

map

from

this

serverProperties

::

getPort

)。

to

factory

::

setPort

);

map

from

this

serverProperties

::

getAddress

)。

to

factory

::

setAddress

);

map

from

this

serverProperties

getServlet

()::

getContextPath

)。

to

factory

::

setContextPath

);

map

from

this

serverProperties

getServlet

()::

getApplicationDisplayName

)。

to

factory

::

setDisplayName

);

map

from

this

serverProperties

getServlet

()::

isRegisterDefaultServlet

)。

to

factory

::

setRegisterDefaultServlet

);

map

from

this

serverProperties

getServlet

()::

getSession

)。

to

factory

::

setSession

);

map

from

this

serverProperties

::

getSsl

)。

to

factory

::

setSsl

);

map

from

this

serverProperties

getServlet

()::

getJsp

)。

to

factory

::

setJsp

);

map

from

this

serverProperties

::

getCompression

)。

to

factory

::

setCompression

);

map

from

this

serverProperties

::

getHttp2

)。

to

factory

::

setHttp2

);

map

from

this

serverProperties

::

getServerHeader

)。

to

factory

::

setServerHeader

);

map

from

this

serverProperties

getServlet

()::

getContextParameters

)。

to

factory

::

setInitParameters

);

map

from

this

serverProperties

getShutdown

())。

to

factory

::

setShutdown

);

for

WebListenerRegistrar

registrar

this

webListenerRegistrars

{

registrar

register

factory

);

}

if

(!

CollectionUtils

isEmpty

this

cookieSameSiteSuppliers

))

{

factory

setCookieSameSiteSuppliers

this

cookieSameSiteSuppliers

);

}

}

ServletWebServerFactoryCustomizer這個Bean是哪裡的?

在我們自動配置類ServletWebServerFactoryAutoConfiguration中定義。

@Bean

public

ServletWebServerFactoryCustomizer

servletWebServerFactoryCustomizer

ServerProperties

serverProperties

ObjectProvider

<

WebListenerRegistrar

>

webListenerRegistrars

ObjectProvider

<

CookieSameSiteSupplier

>

cookieSameSiteSuppliers

{

return

new

ServletWebServerFactoryCustomizer

serverProperties

webListenerRegistrars

orderedStream

()。

collect

Collectors

toList

()),

cookieSameSiteSuppliers

orderedStream

()。

collect

Collectors

toList

()));

}

推薦文章