用實戰(zhàn)》第 11 章 嵌入式容器 閱讀筆記 29)
《SpringBoot 3入門與應(yīng)用實戰(zhàn)》第 11 章 嵌入式容器 閱讀筆記 29對于傳統(tǒng)的 Web 應(yīng)用而言在開發(fā)完畢后需要將工程打包成 war 包部署到獨立的 Web 容器中如 Tomcat、Jetty、Undertow 等運行工程時也需要啟動獨立的 Web 容器。而在前面兩章的學(xué)習(xí)中自始至終都沒有將 Spring Boot 應(yīng)用部署到外置的 Web 容器中這背后的功臣是 Spring Boot 整合的嵌入式 Web 容器。有了嵌入式 Web 容器啟動 Spring Boot 的Web應(yīng)用時便可以獨立啟動而不再依賴任何外置 Web 容器。本章主要研究的就是 Spring Boot 內(nèi)部集成的嵌入式容器整體來講難度不大以熟悉為主。11.1 Web 容器對比首先對外置 Web 容器與嵌入式 Web 容器進(jìn)行簡單了解和對比。僅從名稱上也能分辨出外置 Web 容器是作為操作系統(tǒng)中一個可執(zhí)行程序出現(xiàn)的而嵌入式 Web 容器則會嵌入應(yīng)用程序中成為其一部分整合嵌入式Web容器的應(yīng)用不借助外部容器和資源即可獨立啟動 Web 應(yīng)用。也正因如此嵌入式 Web 容器支持成為 Spring Boot 最強(qiáng)大的特性之一。以 Tomcat 為例普通的外置 Tomcat 與嵌入式 Tomcat 從核心、本質(zhì)上看沒有任何區(qū)別它們都可以承載 Web 應(yīng)用的運行但是 Spring Boot整合嵌入式 Tomcat 容器時考慮了一些特殊的因素它會在底層設(shè)定一些額外的限制需要讀者簡單了解。部署應(yīng)用的限制。由于嵌入式 Tomcat 不是獨立的 Web 容器它需要嵌入特定應(yīng)用中該特性使得嵌入式 Tomcat 只能一次部署一個 Web 應(yīng)用。web.xml 的限制。Spring Boot 整合嵌入式 Tomcat 后不再對 web.xml 文件予以支持轉(zhuǎn)而使用 Bean 配合 ServletRegistrationBean 實現(xiàn)Servlet、Filter、Listener 的編程式注冊對應(yīng) 10.3.6 節(jié)中講解的內(nèi)容?。原生 Servlet 三大組件的限制。原生的基于 Servlet 3.0 及以上規(guī)范的 Web 項目其類路徑下的 Servlet、Filter、Listener 可以被自動掃描并注冊而 Spring Boot 整合嵌入式 Tomcat 后該特性會消失如果需要開啟此特性需要配合 ServletComponentScan 使用對應(yīng) 10.3.6 節(jié)中講解的內(nèi)容?。JSP 的限制。Spring Boot 整合嵌入式 Tomcat 后如果以獨立 jar 包的方式啟動項目則項目中編寫的 JSP 會失效如果以 war 包的方式部署到外置 Tomcat 容器則 JSP 可以正常運行出現(xiàn)該現(xiàn)象的原因是嵌入式 Tomcat 沒有引入 JSP 引擎依賴?。11.2 使用嵌入式 Tomcat在默認(rèn)情況下當(dāng) Spring Boot 引入 WebMvc 依賴時會自動將嵌入式 Tomcat 引入工程可以借助 IDEA 的 Maven 依賴工具查看??梢钥吹絊pring Boot 4.1.1 版本中依賴的 Tomcat 版本為 11.0.24??梢钥吹?3 個嵌入式 Tomcat 的依賴包分別是核心包 core、支持 EL 表達(dá)式的 el 包以及支持 websocket 的包。對 Spring Boot 引入嵌入式Tomcat 而言這 3 個依賴包已經(jīng)完全足夠。從控制臺的輸出中也能看出引入的 Tomcat 版本并且默認(rèn)運行在 8080 端口。仔細(xì)觀察日志的內(nèi)容還可以發(fā)現(xiàn)其實日志中已經(jīng)體現(xiàn)出使用的 ApplicationContext 也適配了嵌入式 Web 容器的實現(xiàn)第6行?。11.3 定制嵌入式容器默認(rèn)情況下雖然使用嵌入式 Web 容器后不需要做什么配置就可以用于開發(fā)但置于真實的生產(chǎn)環(huán)境中時還是需要一些配置比如端口的配置、SSL 證書的配置等。Spring Boot 提供了兩種定制嵌入式 Web 容器的方式下面分別講解。11.3.1 修改配置屬性Spring Boot 提供的全局配置文件針對嵌入式 Web 容器已經(jīng)提供了非常多的配置屬性直接在 application.properties 中配置即可它們大多以server.* 格式為主代碼展示了部分配置屬性的設(shè)置包括端口設(shè)置、上下文路徑修改等還有針對 Tomcat 單獨設(shè)置的配置屬性包括 Tomcat的工作線程數(shù)量、最大連接數(shù)、靜態(tài)資源緩存等。server.port9090 server.servlet.context-path/embedded server.servlet.session.timeout60m# 最小工作線程數(shù)量默認(rèn)10server.tomcat.threads.min-spare20# 最大工作線程數(shù)量默認(rèn)200server.tomcat.threads.max500# 日志保存的最大日期默認(rèn)-1代表永久保存server.tomcat.accesslog.max-days15# form表單最大允許提交的大小默認(rèn)2server.tomcat.max-http-form-post-size2MB# 最大連接數(shù)默認(rèn)8192server.tomcat.max-connections8192# 是否開啟靜態(tài)資源緩存默認(rèn)開啟server.tomcat.resource.allow-cachingtrue# 靜態(tài)資源的緩存過期時間默認(rèn)5秒server.tomcat.resource.cache-ttl30m值得關(guān)注的一個配置屬性是 server.port通過這個屬性可以修改嵌入式 Web 容器的端口號如果不做任何配置那么 Web 容器會默認(rèn)運行在8080 端口上。通常來講不推薦將端口號設(shè)置為 1024 以內(nèi)的數(shù)值而將端口設(shè)置為 0 時會隨機(jī)產(chǎn)生一個與當(dāng)前系統(tǒng)不沖突的端口號作為 Web容器的監(jiān)聽端口。11.3.2 使用定制器如果 Spring Boot 提供的配置屬性不足以滿足訴求還可以使用 Spring Boot 提供的另一種方式定制器。如果說配置屬性的方式屬于聲明式配置那么定制器的方式就可以稱作編程式配置。相較于使用配置屬性而言使用定制器的特點是更加靈活但同時也增加了代碼量。下面用一個簡單示例來演示定制器的使用對嵌入式 Web 容器而言使用的定制器是一個固定的接口 WebServerFactoryCustomizer它需要指定一個定制的泛型關(guān)于這個泛型可以選擇與具體容器無關(guān)的 ConfigurableWebServerFactory也可以是當(dāng)前工程中引入的某個嵌入式容器的派生子類如 Tomcat 對應(yīng)的派生類是 ConfigurableTomcatWebServerFactory?。如果指定的泛型是 ConfigurableWebServerFactory那么在定制器的內(nèi)部中調(diào)用的方法都是嵌入式 Web 容器共有的屬性如端口號、SSL 配置、HTTP2 配置等而如果指定的是具體派生類如ConfigurableTomcatWebServerFactory那么可以調(diào)用更多的與 Tomcat 相關(guān)的方法以供更加細(xì)致地定制嵌入式Tomcat。packagecom.yangjunbo.springbootembedded.container.config;importorg.springframework.boot.tomcat.ConfigurableTomcatWebServerFactory;importorg.springframework.boot.web.server.ConfigurableWebServerFactory;importorg.springframework.boot.web.server.WebServerFactoryCustomizer;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/** * ClassName: WebServerConfigure * Package: com.yangjunbo.springbootembedded.container.config * Description: * * Author 楊鈞博 * Create 2026/9/2 10:31 * Version 1.0 */ConfigurationpublicclassWebServerConfigure{BeanpublicWebServerFactoryCustomizerConfigurableWebServerFactorywebServerFactoryCustomizer(){returnfactory-{factory.set};}}packagecom.yangjunbo.springbootembedded.container.config;importorg.springframework.boot.tomcat.ConfigurableTomcatWebServerFactory;importorg.springframework.boot.web.server.ConfigurableWebServerFactory;importorg.springframework.boot.web.server.WebServerFactoryCustomizer;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/** * ClassName: WebServerConfigure * Package: com.yangjunbo.springbootembedded.container.config * Description: * * Author 楊鈞博 * Create 2026/9/2 10:31 * Version 1.0 */ConfigurationpublicclassWebServerConfigure{BeanpublicWebServerFactoryCustomizerConfigurableTomcatWebServerFactorywebServerFactoryCustomizer(){returnfactory-{factory.set};}}packagecom.yangjunbo.springbootembedded.container.config;//import org.springframework.boot.tomcat.ConfigurableTomcatWebServerFactory;//import org.springframework.boot.web.server.ConfigurableWebServerFactory;//import org.springframework.boot.web.server.WebServerFactoryCustomizer;//import org.springframework.context.annotation.Bean;//import org.springframework.context.annotation.Configuration;/** * ClassName: WebServerConfigure * Package: com.yangjunbo.springbootembedded.container.config * Description: * * Author 楊鈞博 * Create 2026/9/2 10:31 * Version 1.0 *///ConfigurationpublicclassWebServerConfigure{// Bean// public WebServerFactoryCustomizerConfigurableTomcatWebServerFactory webServerFactoryCustomizer() {// return factory - {// factory.set// };// }}請讀者注意這個定制器中的一個概念WebServer。在 Spring Boot 的內(nèi)部所有嵌入式 Web 容器統(tǒng)稱為 WebServer而創(chuàng)建嵌入式 Web 容器的工廠被稱為 WebServerFactory。如果讀者想要更加深入地學(xué)習(xí) Spring Boot 的嵌入式 Web 容器原理和機(jī)制就需要牢記這個概念在筆者的另一本著作《Spring Boot源碼解讀與原理分析》第 8 章中有關(guān)于 WebServer 的詳細(xì)講解感興趣的讀者可以查閱?。11.4 替換嵌入式容器Spring Boot 選擇使用 Tomcat 作為嵌入式 Web 容器的默認(rèn)實現(xiàn)。如果不想使用 Tomcat 運行應(yīng)用還可以選擇 Jetty 或 Undertow 取而代之。替換的方式非常簡單只需要將 spring-boot-starter-web 依賴中默認(rèn)的 spring-boot-starter-tomcat 依賴移除并添加 Jetty 或 Undertow 的依賴代碼演示了替換為 Undertow 的方法。Spring Boot 4.0 起已經(jīng)正式移除了對 Undertow 的支持。原因是 Undertow 未適配 Servlet 6.1 規(guī)范Spring Boot 4 強(qiáng)制依賴該規(guī)范。?xml version1.0 encodingUTF-8?projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion4.1.1/versionrelativePath/!-- lookup parent from repository --/parentgroupIdcom.yangjunbo/groupIdartifactIdspringboot-embeddedcontainer-a/artifactIdversion0.0.1-SNAPSHOT/versionnamespringboot-embeddedcontainer-a/namedescriptionspringboot-embeddedcontainer-a/descriptionurl/licenseslicense//licensesdevelopersdeveloper//developersscmconnection/developerConnection/tag/url//scmpropertiesjava.version17/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webmvc/artifactIdexclusionsexclusiongroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-tomcat/artifactId/exclusion/exclusions/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jetty/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webmvc-test/artifactIdscopetest/scope/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project替換完畢后重新運行主啟動類可以發(fā)現(xiàn)控制臺中打印了 Jetty 容器的啟動日志說明嵌入式 Web 容器已經(jīng)被成功替換。11.5 SSL 配置目前為止使用的嵌入式 Web 容器提供的都是 HTTP 服務(wù)由于這種協(xié)議在數(shù)據(jù)傳輸時使用明文因此是一種不安全的協(xié)議??梢允褂?HTTPS來取代 HTTP 進(jìn)行相對安全的通信。HTTPS 可以理解為 HTTPSSL它利用 SSL 的加密機(jī)制使傳輸?shù)臄?shù)據(jù)被加密處理因此 HTTPS 被更多地應(yīng)用于金融、電商等數(shù)據(jù)敏感等級較高的網(wǎng)站。Spring Boot 提供了非常方便的 SSL 配置方式通過 server.ssl.* 系列配置就可以快速實現(xiàn)。SSL 配置的前提是擁有一個 SSL 證書讀者可以利用 JDK 提供的 keytool 工具生成一個本地測試用的證書也可以通過向?qū)iT的服務(wù)商如國內(nèi)的云服務(wù)提供商阿里云、華為云、騰訊云等申請獲得證書。下面演示使用 keytool 工具生成證書。打開一個命令行窗口輸入命令 keytool -genkey -alias httpstest -keyalg RSA -storetype PKCS12 -keystore httpstest.p12 后按 Enter 鍵會看到命令行窗口提示輸入密鑰庫口令按照提示依次輸入口令、姓名、組織等信息后即可生成一個密鑰整個過程都是交互式的操作起來比較方便。最終生成的證書文件會在當(dāng)前命令行提示的文件夾中。keytool-genkey-aliashttpstest-keyalgRSA-storetypePKCS12-keystorehttpstest.p12使用 keytool 生成的證書僅適用于本地測試學(xué)習(xí)不建議將該證書應(yīng)用于生產(chǎn)環(huán)境因為自行生成的證書存在諸多安全隱患容易被不法分子攻擊。如果需要相對可靠的證書需要去云服務(wù)商申請。拿到證書文件 httpstest.p12 后接下來將其復(fù)制到工程的 resources 目錄下并在 application.yaml 中配置 SSL 證書的信息即可。注意配置項 server.ssl.key-store-password 的內(nèi)容就是生成SSL證書時填入的密鑰庫口令。spring:application:name:springboot-embeddedcontainer-aserver:port:8443ssl:key-store:classpath:httpstest.p12key-store-password:123456之后使用 Chrome 訪問 https://localhost:8443會發(fā)現(xiàn) Chrome 彈出了連接不安全的提示如果繼續(xù)訪問則瀏覽器的地址欄上會標(biāo)注 “不安全”字樣這印證了剛才說的自建 SSL 證書不建議用于生產(chǎn)環(huán)境。注意當(dāng)配置 SSL 之后嵌入式 Web 容器將不再支持 HTTP 請求的訪問如果需要同時支持 HTTP 和 HTTPS則需要單獨添加相應(yīng)的配置。代碼提供了基于嵌入式 Tomcat 的 HTTP 與 HTTPS 兼顧方案它向 IOC 容器中注冊了一個 TomcatServletWebServerFactory 組件這個組件可以讓 Tomcat 多監(jiān)聽一個 8080 端口這樣就可以同時使用 https://localhost:8443 訪問 HTTPS 的端口使用 http://localhost:8080 訪問 HTTP的端口。編寫完畢后重啟應(yīng)用在控制臺中可以看到同時支持兩個協(xié)議的日志證明雙協(xié)議支持已經(jīng)完成。packagecom.yangjunbo.springbootembedded.container.config;//import org.springframework.boot.tomcat.ConfigurableTomcatWebServerFactory;//import org.springframework.boot.web.server.ConfigurableWebServerFactory;//import org.springframework.boot.web.server.WebServerFactoryCustomizer;//import org.springframework.context.annotation.Bean;//import org.springframework.context.annotation.Configuration;importorg.apache.catalina.connector.Connector;importorg.apache.catalina.startup.Tomcat;importorg.apache.coyote.http11.Http11NioProtocol;importorg.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;importorg.springframework.boot.web.server.servlet.ServletWebServerFactory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/** * ClassName: WebServerConfigure * Package: com.yangjunbo.springbootembedded.container.config * Description: * * Author 楊鈞博 * Create 2026/9/2 10:31 * Version 1.0 */ConfigurationpublicclassWebServerConfigure{// Bean// public WebServerFactoryCustomizerConfigurableTomcatWebServerFactory webServerFactoryCustomizer() {// return factory - {// factory.set// };// }BeanpublicServletWebServerFactoryhttpServletWebServerFactory(){TomcatServletWebServerFactoryfactorynewTomcatServletWebServerFactory();ConnectorconnectornewConnector(newHttp11NioProtocol());connector.setPort(8080);factory.addAdditionalConnectors(connector);returnfactory;}}11.6 小結(jié)嵌入式 Web 容器與普通的外置 Web 容器在運行效果上大致相同但也存在一些差異比如部署應(yīng)用的限制、原生組件的限制等Spring Boot默認(rèn)使用 Tomcat 作為嵌入式 Web 容器的實現(xiàn)同時還支持 Jetty、Undertow 和響應(yīng)式的 Netty (WebFlux)Spring Boot 同時提供了全局配置屬性和定制器兩種方式對嵌入式 Web 容器進(jìn)行定制并可以根據(jù)引入的不同 Web 容器類型編寫對應(yīng)的配置項。