您現在的位置是:首頁 > 藝術
「Spring boot」整合tomcat底層原理
server物件有什麼作用
本文轉載自【微信公眾號:程式JAVA圈】經微信公眾號授權轉載,如需轉載請與原文作者聯絡。
本文結論
原始碼基於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。
內部的starter-tomcat。png
內部的tomcat-embed-core。png
自動配置類:ServletWebServerFactoryAutoConfiguration
在spring-boot-autoconfigure-2。6。6。jar這個包中的spring。factories檔案內,配置了大量的自動配置類,其中就包括自動配置tomcat的自動配置類:ServletWebServerFactoryAutoConfiguration
自動配置類的配置。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物件。
ServletWebServerFactory介面。png
WebServer擁有啟動、停止、獲取埠等方法,就會發現WebServer其實指的就是Tomcat、Jetty、Undertow。
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
()));
}
推薦文章
- 《火影忍者》裡,寧次中後期實力進展一般般,為什麼?
如果按我的想法的話,日向一族最好的歸縮是醫療忍者,科研人員,而不是戰鬥人員,如果內視能看到人體經絡的話那簡直是最好的內科醫生,即便沒有陽遁學不會醫療忍術作為專業的醫生也比開個白眼上戰場做雷達和人近身接戰好吧,甚至日向如果作為科研人員研發查克...
- 多肉姬朧月容易養成懸崖樁,唐印容易長成“傻大個”,你喜歡哪個
①、姬朧月耐曬容易養成懸崖樁唐印和姬朧月,生長期需要充足的光照,特別是唐印,光照不足葉片會變稀不緊湊...
- 蒲公英根茶的功效與作用,蒲公英根茶和什麼調配最好
蒲公英根茶的功效與作用清熱解毒蒲公英根是一種寒涼的中藥材,具有很好的清熱解毒的作用,這也是最主要的成效之一,在臨床上能夠輔佐各種熱毒症,尤其是肝炎亢進或肝臟熱時能夠直接服用蒲公英根茶...