日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

springboot启动没反应_新特性:Tomcat和Jetty如何处理Spring Boot应用?

發布時間:2024/9/19 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot启动没反应_新特性:Tomcat和Jetty如何处理Spring Boot应用? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為了方便開發和部署,Spring Boot 在內部啟動了一個嵌入式的 Web 容器。我們知道 Tomcat 和 Jetty 是組件化的設計,要啟動 Tomcat 或者 Jetty 其實就是啟動這些組件。在 Tomcat 獨立部署的模式下,我們通過 startup 腳本來啟動 Tomcat,Tomcat 中的 Bootstrap 和 Catalina 會負責初始化類加載器,并解析server.xml和啟動這些組件。

在內嵌式的模式下,Bootstrap 和 Catalina 的工作就由 Spring Boot 來做了,Spring Boot 調用了 Tomcat 和 Jetty 的 API 來啟動這些組件。那 Spring Boot 具體是怎么做的呢?而作為程序員,我們如何向 SpringBoot 中的 Tomcat 注冊 Servlet 或者 Filter 呢?我們又如何定制內嵌式的 Tomcat?今天我們就來聊聊這些話題。

Spring Boot 中 Web 容器相關的接口

既然要支持多種 Web 容器,Spring Boot 對內嵌式 Web 容器進行了抽象,定義了WebServer接口:

public interface WebServer { void start() throws WebServerException; void stop() throws WebServerException; int getPort();}復制代碼

各種 Web 容器比如 Tomcat 和 Jetty 需要去實現這個接口。

Spring Boot 還定義了一個工廠ServletWebServerFactory來創建 Web 容器,返回的對象就是上面提到的 WebServer。

public interface ServletWebServerFactory { WebServer getWebServer(ServletContextInitializer... initializers);}復制代碼

可以看到 getWebServer 有個參數,類型是ServletContextInitializer。它表示 ServletContext 的初始化器,用于 ServletContext 中的一些配置:

public interface ServletContextInitializer { void onStartup(ServletContext servletContext) throws ServletException;}復制代碼

這里請注意,上面提到的 getWebServer 方法會調用 ServletContextInitializer 的 onStartup 方法,也就是說如果你想在 Servlet 容器啟動時做一些事情,比如注冊你自己的 Servlet,可以實現一個 ServletContextInitializer,在 Web 容器啟動時,Spring Boot 會把所有實現了 ServletContextInitializer 接口的類收集起來,統一調它們的 onStartup 方法。

為了支持對內嵌式 Web 容器的定制化,Spring Boot 還定義了WebServerFactoryCustomizerBeanPostProcessor接口,它是一個 BeanPostProcessor,它在 postProcessBeforeInitialization 過程中去尋找 Spring 容器中 WebServerFactoryCustomizer

類型的 Bean,并依次調用 WebServerFactoryCustomizer

接口的 customize 方法做一些定制化。

public interface WebServerFactoryCustomizer<T extends WebServerFactory> { void customize(T factory);}復制代碼

內嵌式 Web 容器的創建和啟動

鋪墊了這些接口,我們再來看看 Spring Boot 是如何實例化和啟動一個 Web 容器的。我們知道,Spring 的核心是一個 ApplicationContext,它的抽象實現類 AbstractApplicationContext

實現了著名的refresh方法,它用來新建或者刷新一個 ApplicationContext,在 refresh 方法中會調用 onRefresh 方法,AbstractApplicationContext 的子類可以重寫這個方法 onRefresh 方法,來實現特定 Context 的刷新邏輯,因此 ServletWebServerApplicationContext 就是通過重寫 onRefresh 方法來創建內嵌式的 Web 容器,具體創建過程是這樣的:

@Overrideprotected void onRefresh() { super.onRefresh(); try { // 重寫 onRefresh 方法,調用 createWebServer 創建和啟動 Tomcat createWebServer(); } catch (Throwable ex) { }} //createWebServer 的具體實現private void createWebServer() { // 這里 WebServer 是 Spring Boot 抽象出來的接口,具體實現類就是不同的 Web 容器 WebServer webServer = this.webServer; ServletContext servletContext = this.getServletContext(); // 如果 Web 容器還沒創建 if (webServer == null && servletContext == null) { // 通過 Web 容器工廠來創建 ServletWebServerFactory factory = this.getWebServerFactory(); // 注意傳入了一個 "SelfInitializer" this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); } else if (servletContext != null) { try { this.getSelfInitializer().onStartup(servletContext); } catch (ServletException var4) { ... } } this.initPropertySources();}復制代碼

再來看看 getWebSever 具體做了什么,以 Tomcat 為例,主要調用 Tomcat 的 API 去創建各種組件:

public WebServer getWebServer(ServletContextInitializer... initializers) { //1. 實例化一個 Tomcat,可以理解為 Server 組件。 Tomcat tomcat = new Tomcat(); //2. 創建一個臨時目錄 File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); //3. 初始化各種組件 Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); //4. 創建定制版的 "Context" 組件。 this.prepareContext(tomcat.getHost(), initializers); return this.getTomcatWebServer(tomcat);}復制代碼

你可能好奇 prepareContext 方法是做什么的呢?這里的 Context 是指Tomcat 中的 Context 組件,為了方便控制 Context 組件的行為,Spring Boot 定義了自己的 TomcatEmbeddedContext,它擴展了 Tomcat 的 StandardContext:

class TomcatEmbeddedContext extends StandardContext {}復制代碼

注冊 Servlet 的三種方式

1. Servlet 注解

在 Spring Boot 啟動類上加上 @ServletComponentScan 注解后,使用 @WebServlet、@WebFilter、@WebListener 標記的 Servlet、Filter、Listener 就可以自動注冊到 Servlet 容器中,無需其他代碼,我們通過下面的代碼示例來理解一下。

@SpringBootApplication@ServletComponentScanpublic class xxxApplication{}復制代碼 @WebServlet("/hello")public class HelloServlet extends HttpServlet {}復制代碼

在 Web 應用的入口類上加上 @ServletComponentScan, 并且在 Servlet 類上加上 @WebServlet,這樣 SpringBoot 會負責將 Servlet 注冊到內嵌的 Tomcat 中。

2. ServletRegistrationBean

同時 Spring Boot 也提供了 ServletRegistrationBean、FilterRegistrationBean 和 ServletListenerRegistrationBean 這三個類分別用來注冊 Servlet、Filter、Listener。假如要注冊一個 Servlet,可以這樣做:

@Beanpublic ServletRegistrationBean servletRegistrationBean() { return new ServletRegistrationBean(new HelloServlet(),"/hello");}復制代碼

這段代碼實現的方法返回一個 ServletRegistrationBean,并將它當作 Bean 注冊到 Spring 中,因此你需要把這段代碼放到 Spring Boot 自動掃描的目錄中,或者放到 @Configuration 標識的類中。

3. 動態注冊

你還可以創建一個類去實現前面提到的 ServletContextInitializer 接口,并把它注冊為一個 Bean,Spring Boot 會負責調用這個接口的 onStartup 方法。

@Componentpublic class MyServletRegister implements ServletContextInitializer { @Override public void onStartup(ServletContext servletContext) { //Servlet 3.0 規范新的 API ServletRegistration myServlet = servletContext .addServlet("HelloServlet", HelloServlet.class); myServlet.addMapping("/hello"); myServlet.setInitParameter("name", "Hello Servlet"); } }復制代碼

這里請注意兩點:

  • ServletRegistrationBean 其實也是通過 ServletContextInitializer 來實現的,它實現了 ServletContextInitializer 接口。
  • 注意到 onStartup 方法的參數是我們熟悉的 ServletContext,可以通過調用它的 addServlet 方法來動態注冊新的 Servlet,這是 Servlet 3.0 以后才有的功能。

Web 容器的定制

我們再來考慮一個問題,那就是如何在 Spring Boot 中定制 Web 容器。在 Spring Boot 2.0 中,我們可以通過兩種方式來定制 Web 容器。

第一種方式是通過通用的 Web 容器工廠 ConfigurableServletWebServerFactory,來定制一些 Web 容器通用的參數:

@Componentpublic class MyGeneralCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { public void customize(ConfigurableServletWebServerFactory factory) { factory.setPort(8081); factory.setContextPath("/hello"); }}復制代碼

第二種方式是通過特定 Web 容器的工廠比如 TomcatServletWebServerFactory 來進一步定制。下面的例子里,我們給 Tomcat 增加一個 Valve,這個 Valve 的功能是向請求頭里添加 traceid,用于分布式追蹤。TraceValve 的定義如下:

class TraceValve extends ValveBase { @Override public void invoke(Request request, Response response) throws IOException, ServletException { request.getCoyoteRequest().getMimeHeaders(). addValue("traceid").setString("1234xxxxabcd"); Valve next = getNext(); if (null == next) { return; } next.invoke(request, response); } }復制代碼

跟第一種方式類似,再添加一個定制器,代碼如下:

@Componentpublic class MyTomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> { @Override public void customize(TomcatServletWebServerFactory factory) { factory.setPort(8081); factory.setContextPath("/hello"); factory.addEngineValves(new TraceValve() ); }}復制代碼

本期精華

今天我們學習了 Spring Boot 如何利用 Web 容器的 API 來啟動 Web 容器、如何向 Web 容器注冊 Servlet,以及如何定制化 Web 容器,除了給 Web 容器配置參數,還可以增加或者修改 Web 容器本身的組件。

課后思考

我在文章中提到,通過 ServletContextInitializer 接口可以向 Web 容器注冊 Servlet,那 ServletContextInitializer 跟 Tomcat 中的 ServletContainerInitializer 有什么區別和聯系呢?

不知道今天的內容你消化得如何?如果還有疑問,請大膽的在留言區提問,也歡迎你把你的課后思考和心得記錄下來,與我和其他同學一起討論。如果你覺得今天有所收獲,歡迎你把它分享給你的朋友。

總結

以上是生活随笔為你收集整理的springboot启动没反应_新特性:Tomcat和Jetty如何处理Spring Boot应用?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。