JavaRush /Java Blog /Random-TW /從 Hello World 到 Spring Web MVC 以及 servlet 與它有什麼關係
Viacheslav
等級 3

從 Hello World 到 Spring Web MVC 以及 servlet 與它有什麼關係

在 Random-TW 群組發布
從 Hello World 到 Spring Web MVC 以及 servlet 與之有何關係 - 1

介紹

眾所周知,Java 的成功正是得益於致力於連接網路的軟體的發展。因此,我們將以通常的控制台應用程式「Hello World」為基礎,了解它從控制台應用程式變成網頁應用程式需要什麼。因此,首先您需要建立一個 Java 專案。程式設計師都是懶惰的人。在史前時代,當一些人在狩獵猛獁象時,其他人則坐著努力避免對各種各樣的 Java 庫和目錄結構感到困惑。為了讓開發人員可以控制創建應用程式的過程,以便他可以簡單地寫“我想要一個這樣那樣的版本2的庫”,他們想出了特殊的工具——構建系統。最著名的兩款是MavenGradle。在本文中,我們將使用 Gradle。如果早些時候我們必須自己建立目錄結構,現在 Gradle 使用Gradle Init 插件,允許我們在一個命令中建立一個具有目錄結構和基底 Main 類別的 Java 專案: gradle init --type java-application 此命令執行初始化 (init)我們是一個帶有控制台Hello World 的Java 應用程式(java-application )。完成後,目錄中會出現一個檔案-build.gradle。這是我們的建置腳本- 即用於建立應用程式的特定腳本,並描述了為此需要執行哪些操作。讓我們打開它並在其中添加以下行:jar.baseName = 'webproject' Gradle 允許您對專案執行各種操作,這些操作稱為任務。透過執行命令(任務),將gradle build/build/libs目錄中建立 JAR 檔案。而且,正如您所猜測的,它的名稱現在將是webproject.jar。但如果我們執行java -jar ./build/libs/webproject.jar,我們會得到一個錯誤:no main manifest attribute。這是因為對於 java 應用程序,您需要附加一個清單 - 這是關於如何使用該應用程式、如何感知它的描述。然後,將執行java應用程式的JVM將知道哪個類別是程式的入口點以及其他資訊(例如,類別路徑)。如果我們仔細查看建置腳本的內容,我們將看到正在連接的插件。例如:apply plugin: 'java' 如果我們進入Gradle Java Plugin 頁面,可以看到我們可以設定清單:
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
主類,即程式的入口點,是由 Gradle Init Plugin 為我們產生的。它甚至在 mainClassName 參數中指定。但這不適合我們,因為...... 此設定引用另一個插件Gradle Application Plugin。因此,我們有一個在螢幕上顯示 Hello World 的 Java 應用程式。該 Java 應用程式打包在 JAR (Java ARchive) 中。它很簡單,基於控制台,不是最新的。如何將其變成Web應用程式?
От Hello World до Spring Web MVC и при чём тут сервлеты - 2

服務程式介面

為了讓Java能夠與網路一起工作,古代就出現了一種叫做Servlet API的規格。該規範描述了客戶端-伺服器互動、從客戶端(例如瀏覽器)接收訊息並發送回應(例如,使用頁面文字)。當然,從那時起,發生了很多變化,但重點是,為了使 Java 應用程式成為 Web 應用程序,需要使用 Servlet API。為了避免毫無根據的推測,讓我們選擇這個規範:JSR-000340 JavaTM Servlet 3.1。首先,我們感興趣的是「第一章:概述」。它描述了我們必須理解的基本概念。首先,什麼是servlet?「 1.1 什麼是 Servlet? 」一章說,Servlet是一個由容器管理並產生動態內容的 Java 元件。與其他 Java 元件一樣,servlet 是一個 Java 類,它被編譯為字節碼,並且可以使用 Java 技術載入到 Web 伺服器中。Servlet 在請求/回應範例的框架內與 Web 用戶端(例如瀏覽器)進行互動非常重要,該範例由 Servlet 容器實作。事實證明,Servlet 存在於某種 Servlet 容器中。這是什麼?在「 1.2 什麼是 Servlet 容器? 」一章中,提到Servlet 容器是 Web 伺服器或應用程式伺服器的一部分,它提供網路服務,透過這些服務發送請求和發送回應。這個 Servlet 容器管理 servlet 的生命週期。所有 Servlet 容器都必須至少支援 HTTP 協議,但也可以支援其他協定。例如,HTTPS。同樣重要的是,Servlet 容器可以對執行 servlet 的環境施加任何與安全相關的限制。同樣重要的是,根據“ 10.6 Web 應用程式存檔檔案”,Web 應用程式必須打包在 WAR(Web 存檔)檔案中。也就是說,現在我們需要刪除 jar 和應用程式插件以進行其他操作。這就是Gradle WAR 插件。並指定 war.baseName 而非 jar.baseName 因為 由於我們不再使用 jar 插件,因此我們也刪除了清單設定。當我們啟動 JAR 時,需要透過清單告訴 Java 虛擬機器 (JVM) 如何使用我們的應用程式。因為 JVM 正在運行它。顯然,Web 應用程式是由某種 Web 伺服器執行的。事實證明,他需要以某種方式告訴他如何使用我們的 Web 應用程式?事實證明是的。Web 應用程式有自己特殊的宣言。它稱為部署描述符。有一整節專門介紹它:「14.部署描述符」。有一個重要的章節:「第10章:」。它從Servlet API的角度講了什麼是Web應用程式。例如,在「10.5目錄結構」一章中指出了部署描述符應該在哪裡:/WEB-INF/web.xmlWEB-INF放在哪裡?正如Gradle WAR 插件中所述,它添加了一個新的佈局src/main/webapp。因此,讓我們創建一個這樣的目錄,在裡面我們將創建一個 WEB-INF 目錄,在裡面我們將創建一個 web.xml 文件。重要的是該目錄稱為WEB -INF,而不是META-INF!讓我們從「14.5.1 基本範例」XML 範例複製它:
От Hello World до Spring Web MVC и при чём тут сервлеты - 3
正如我們所看到的,XML 文件用於配置。一個 XML 文檔,為了被認為是有效的(Valid),必須符合一些「模式」。您可以將其視為 XML 文件的一種介面。模式指定 XML 文件中可以包含哪些元素、可定義元素的資料類型、順序、要求以及其他方面。從文件複製的範例指示版本 2.5,但我們想使用版本 3.1。當然,規格會隨著版本的變化而變化,並添加新功能。因此,您需要使用 2.5 版 (web-app_2_5.xsd) 所用架構以外的架構。3.1版本應該使用什麼方案?文件將幫助我們解決這個問題,章節“ 14.3部署描述符”,其中指出specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd ,也就是說,我們需要在各處用指定的xsd替換模式的鏈接,不要忘記將其更改version="2.5"為3.1,並且還要更改各處的命名空間( xmlns 和 xsi:schemaLocation 中)。它們指示我們將在哪個命名空間內工作(簡單地說,我們可以使用哪些元素名稱)。如果開啟架構文件,targetNamespace 將包含我們應該指定的相同命名空間:
От Hello World до Spring Web MVC и при чём тут сервлеты - 4
我們記得,在 Jar 檔案的清單中,我們編寫了要使用的類別。在這裡做什麼?這裡我們需要指定當我們收到來自 Web 用戶端的請求時要使用哪個 servlet 類別。詳細說明可參考「14.4 部署描述符圖」一章。它看起來像這樣:
От Hello World до Spring Web MVC и при чём тут сервлеты - 5
這裡一切都很簡單。宣告serverlet,然後將其對應到某個模板。在本例中,在 /app 上。當模板執行時,servlet方法將會執行。為了美觀,應該將App類別轉移到套件中,同時不要忘記更正xml配置。但這還不是全部。應用程式必須是 servlet。成為 servlet 意味著什麼?這意味著我們必須繼承自HttpServlet一個範例可以在「 8.1.1 @WebServlet 」章節中看到。根據它,我們的 App 類別將如下所示:
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
但我們的項目還沒準備好。因為我們現在依賴Servlet API 3.1版本。這意味著在我們的建置腳本中,我們需要指示對 Servlet API 的依賴。JVM需要知道你在程式碼中寫的內容是否正確以及如何使用它。我們記得,規範本質上只是描述其如何運作的介面。實作位於網路伺服器端。因此,如果沒有 Servlet API,就會在 Maven Central 上找到所需的函式庫:javax.servlet-api。並在依賴項區塊中新增一個條目。在 Maven 儲存庫中,如您所看到的,它顯示「已提供」。在使用依賴項之前,您必須指定範圍。Gradle 沒有名為「provided」的作用域,但它有一個「僅編譯」作用域。因此,我們會表示:providedCompile 'javax.servlet:javax.servlet-api:3.1.0' 呃,好像一切都好吧?Gradle Build 會將我們的專案建置成 WAR 檔案。接下來我們該用它做什麼?首先,我們需要一個 Web 伺服器。在 Google 中,我們編寫「Web 伺服器 Java 清單」並查看 Web 伺服器清單。讓我們從此列表中進行選擇,例如TomCat。前往Apache Tomcat網站,下載最新版本(目前版本 9)作為 zip 檔案(如果適用於 Windows)。將其解壓縮到某個目錄中。萬歲,我們有一個網頁伺服器。從 Web 伺服器目錄的bin子目錄中,我們從命令列執行catalina並查看可用選項。讓我們做:catalina start。每個 Web 伺服器都有一個該 Web 伺服器監視的目錄。如果其中出現 Web 應用程式文件,Web 伺服器就會開始安裝它。這種安裝稱為部署或部署。是的,這就是為什麼“部署描述符”。即如何正確部署應用程式。在 Tomcat 中,該目錄是webapps。讓我們複製使用 gradle build 進行的戰爭。之後,在日誌中我們將看到類似以下內容: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms 為了更好地理解,在 tomcat 目錄中,我們將編輯該文件\conf\tomcat-users.xml,並添加以下行:
От Hello World до Spring Web MVC и при чём тут сервлеты - 6
現在我們重新啟動伺服器(catalina stop,catalina start)並轉到該位址,http://127.0.0.1:8080/manager 這裡我們將看到所有應用程式的路徑。我們的 web 專案很可能被指定為 /webproject 路徑。這條路是什麼?「 10.1 Web 伺服器內的 Web 應用程式」一章中的規格指出,Web 應用程式與應用程式內的某個路徑相關聯(在本例中為 /webproject)。通過此路徑的所有請求都將與同一個 ServletContext 關聯。該路徑也稱為contextRoot。並且根據“ 10.2 與 ServletContext 的關係”,Servlet 容器將 Web 應用程式和 ServletContext 一對一關聯。也就是說,每個Web應用程式都有自己的ServletContext。什麼是ServletContext?如規範所述,ServletContext是一個對象,它為 Servlet 提供運行它們的「應用程式視圖」。Servlet Context在 Servlet API 規格的第 4 章中有更詳細的描述。令人驚訝的是,3.1 版的 Servlet API 不再需要 web.xml。例如,您可以使用註解定義 servlet:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
另外建議的主題是:《Java EE 面試 - JEE Servlet API(問答)》。因此,我們有一個 Servlet - 它負責向 Web 用戶端提供什麼回應。我們有一個 ServletContainer,它接收來自使用者的請求,將存取的路徑與 Servlet 的路徑進行匹配,如果找到匹配,則執行 Servlet。美好的。春天在這幅世界圖景中佔據什麼位置?

Spring Web MVC

太棒了,我們有一個網頁應用程式。現在我們需要連接Spring。我們該怎麼做?首先,您需要弄清楚如何將 Spring 正確連接到您的專案。事實證明,早前按照Spring平台專案的文檔是可以這樣做的,但現在“ The Platform willreached the end of itssupported life on April 9, 2019 ”,即不建議這樣做使用它,因為 很快它將不再受支援。唯一的出路是「鼓勵平台使用者開始使用Spring Boot的依賴管理」。因此,讓我們繼續了解Spring Boot文檔。讓我澄清一下,我們不使用 Spring Boot 本身,而只使用 Spring Boot 的依賴管理。也就是說,Spring Boot 專案可以提供有關使用哪些版本的程式庫(包括 Spring MVC)的知識。在那裡我們會找到3.2。隔離使用Spring Boot的依賴管理。根據文檔,在建置腳本中新增以下內容:
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
正如你所看到的,我們指出apply false,即 我們不使用 Spring Boot 本身,但我們使用那裡的依賴管理。這種依賴性管理也稱為 BOM——「物料清單」。現在我們已準備好連接 Spring Web MVC 專案本身。Spring Web MVC 是Spring Framework專案的一部分,我們對「 Web Servlet 」部分感興趣。讓我們將依賴項新增到建置腳本中compile 'org.springframework:spring-webmvc':正如我們所看到的,我們設定了範圍編譯,因為 Web 伺服器不會為我們提供 Spring。我們的專案被迫在其內部包含 Spring 庫。接下來,我們必須閱讀“ 1.2.DispatcherServlet ”部分,其中提到Spring MVC是圍繞“前端控制器”模式構建的,其中有某種中央 servlet 提供對其他組件的配置和委託。Dispatcher可以翻譯為調度員。因此,首先,在 web.xml 中我們宣告:
От Hello World до Spring Web MVC и при чём тут сервлеты - 7
我們可以看到,這實際上是 Servlet API 規範中定義的常規 Listener。更準確地說,這是一個 ServletContextListener,也就是說,它被觸發來初始化我們的 Web 應用程式的 Servlet Context。接下來,您需要指定一個設置,告訴 Spring 帶有設置的特殊 xml 配置位於何處:
От Hello World до Spring Web MVC и при чём тут сервлеты - 8
正如您所看到的,這只是儲存在 Servlet 上下文層級的常規設置,但 Spring 在初始化應用程式上下文時將使用它。現在您需要聲明一個分發所有其他請求的調度程序,而不是所有 Servlet。
От Hello World до Spring Web MVC и при чём тут сервлеты - 9
這裡沒有魔法。如果我們看一下,它是一個 HttpServlet,Spring 做了很多事情,使其成為一個框架。剩下的就是將特定 URL 模板與 servlet 關聯(映射):
От Hello World до Spring Web MVC и при чём тут сервлеты - 10
一切都和我們以前一樣。現在讓我們創建一些我們的網頁伺服器應該顯示的東西。例如,我們在WEB-INF中建立一個pages子目錄,裡面會有一個檔案hello.jsp。內容可以是最原始的。例如,在 html 標籤內有一個 h1 標籤,其中包含文字「Hello World」。並且不要忘記創建applicationContext.xml我們之前指定的文件。讓我們以 Spring 文件為例:「1.10.3. 自動偵測類別並註冊 bean 定義」。
От Hello World до Spring Web MVC и при чём тут сервлеты - 11
因為 我們以這種方式啟用自動檢測,我們現在可以創建2 個類別(由於使用了特殊的Spring 註釋,它們將被視為Spring Bean),Spring 現在將創建它們自己並在它們的幫助下自訂我們的應用程式:
  1. Web 配置例如 Java 樣式配置:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    Spring 框架文件中描述了此範例:「1.11.MVC Config」。

    這裡我們註冊了一個ViewResolver,它將幫助確定jsp頁面的位置。第二種方法可確保啟用「預設 servlet 」。

    您可以在這裡閱讀更多相關內容:「default-servlet-handler 的需求和用途是什麼」。

  2. HelloController 控制器,用於描述請求到特定 JSP 的映射

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    這裡我們使用了「 1.4.附註解的控制器」一章文件中所描述的@Controller註解。

現在,當部署我們的應用程式時,當我們發送請求/webproject/hello(其中 /webproject 是上下文根)時,將首先處理 DispatcherServlet。他作為主調度程序,會確定我們/*匹配當前請求,這意味著DispatcherServlet必須做一些事情。然後它將遍歷它找到的所有映射。它將看到有一個 HelloController ,其句柄方法會對應到 /hello 並將執行它。此方法將傳回文字“hello”。該文字將由 ViewResolver 接收,它將告訴伺服器到哪裡尋找需要向客戶端顯示的 jsp 檔案。因此,客戶最終將收到那個非常珍貴的頁面。

結論

我希望從這篇文章中可以清楚地看出「背景」這個詞並不可怕。事實證明該規範非常有用。文檔是我們的朋友,而不是我們的敵人。我希望大家能夠清楚 Spring 是基於什麼、它是如何連接的以及 Servlet API 與它有什麼關係。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION