理解本文所需的知識:您已經或多或少地了解了Java Core,並希望了解JavaEE 技術Web 編程對您來說,目前正在研究Java Collections quest最有意義,它處理的主題與本文很接近。
使用 servlet 和 JSP 創建簡單的 Web 應用程序(第 1 部分)- 1
本材料是我的文章在 IntelliJ Idea Enterprise 中創建最簡單的 Web 項目的邏輯延續。在那篇文章中,我演示瞭如何創建一個可用的 Web 項目模板。這次我將向您展示如何使用Java Servlet APIJavaServer Pages API創建一個簡單但非常有吸引力的 Web 應用程序。 我們的應用程序將有一個帶有兩個鏈接的主頁:
  • 添加用戶頁面的鏈接;
  • 指向用戶列表的鏈接。
和以前一樣,我將使用IntelliJ Idea Enterprise EditionApache Maven(我們將只連接一些依賴項)和Apache Tomcat。最後,我們將使用W3.CSS框架“美化”我們的應用程序。我們假設您已經有一個我們現在要添加的空項目。如果沒有,請瀏覽第一篇文章並製作一篇。只需幾分鐘 :)

關於我們未來應用程序結構的一些信息

我們的主頁 (/) 將是一個最普通的靜態HTML頁面,帶有一個標題和兩個鏈接/按鈕:
  • 添加一個新用戶(導航到/添加);
  • 查看用戶列表(導航至 / list)。
Tomcat將捕獲對這些地址的請求並將它們發送到我們將要創建的兩個 servlet 之一(我們將在web.xml中指定映射)。然後 servlet 將處理請求、準備數據(或保存數據,如果我們要添加用戶),並將控制轉移到適當的JSP 文件,然後“呈現”結果。我們將數據存儲在普通列表(List)中。

創建一個靜態主頁

如果您的 web 文件夾中有index.jsp ,請將其刪除。相反,在此文件夾中創建一個名為index.html的簡單HTML 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My super project!</title>
</head>
<body>
    <!-- header -->
    <div>
        <h1>Super app!<//h1>
    </div>

    <div>       <!-- content -->
        <div>    <!-- button holder -->
            <button onclick="location.href='/list'">List users<//button>
            <button onclick="location.href='/add'">Add user<//button>
        </div>
    </div>
</body>
</html>
這裡沒有什麼複雜的。在title標籤中,我們指明了頁面的標題。在頁面主體中,我們有兩個主要的 div:headercontent。內容div包括我們按鈕的支架。我們有兩個按鈕,單擊一下即可將您帶到相應的地址。您可以運行該項目並查看它現在的樣子。如果你點擊按鈕,你會得到404 錯誤頁面, 因為我們對應的頁面還不存在。但我們可以看出按鈕有效。請注意,這不是最通用的方法:如果瀏覽器中的 JavaScript 被關閉,那麼這些按鈕將不起作用。但我們假設沒有人禁用 JavaScript。:) 顯然,您可以使用簡單的鏈接,但我更喜歡按鈕。你可以隨心所欲地做。不要擔心我的例子會有很多div。我們稍後會用樣式填充它們,一切都會看起來更漂亮。:)

創建 JSP 文件以呈現結果

在同一個Web目錄中,創建一個文件夾,我們將在其中添加我們的JSP 文件。我將其稱為“觀點”,但您還是可以即興創作。在此文件夾中,我們將創建兩個 JSP 文件:
  • add.jsp — 添加用戶的頁面;
  • list.jsp — 顯示用戶列表的頁面。
為它們分配適當的頁眉。像“添加新用戶”和“用戶列表”之類的東西,我們就這樣吧。

創建兩個 servlet

Servlet 將接收並處理Tomcat發送給它們的請求。在src/main/java文件夾中,創建應用程序包,我們將在其中放置源代碼。其他包裹也將去那裡。因此,為了防止這些包在彼此內部創建,我們將在應用程序包中創建一些類(稍後我們將刪除它)。現在在應用程序包中創建三個不同的包:
  • 實體——我們的實體(描述用戶對象的類)放在這裡;
  • model——這是我們的模型所在的地方(我們稍後會討論這個);
  • servlet——這就是我們的 servlet 所在的位置。
完成此操作後,您可以冷靜地從應用程序包中刪除該類(當然,如果您創建了它)。在servlets包中,創建兩個類:
  • AddServlet — 處理髮送到 / add的請求;
  • ListServlet — 處理髮送到 / list 的請求。

在 Maven 中連接依賴項

Tomcat 9. * 實現了Servlet 4.0JavaServer Pages 2.3的規範。Tomcat 9官方文檔第一段第二行就是這麼說的。這意味著如果您像我一樣使用這個版本的Tomcat,那麼您將編寫和運行的代碼將使用這些版本。但是我們希望在我們的項目中有這些規範,這樣我們使用它們的代碼至少可以成功編譯。為此,我們需要將它們加載到我們的項目中。這就是Maven來拯救的地方。

一般規則是這樣的:如果您需要使用 Maven 將某些東西連接到您的項目:

  • 從 Maven 轉到存儲庫網站;
  • 查找所需庫的所需版本;
  • 獲取需要粘貼到 pom.xml 中的依賴代碼;
  • 粘貼!:)
讓我們開始。首先,準備POM 文件在/version條目之後但在/project之前的某處,插入以下內容:

<dependencies>

</dependencies>
我們這樣做是為了表明我們將在這些標籤中列出所需的依賴項。現在轉到mvnrepository.com。頂部有一個搜索字段。首先,搜索“ servlet ”。第一個結果,已經被使用了七千多次,很適合我們。請記住,我們需要4.0版(對於Tomcat 9). 其他版本可能適用於舊的實現。這是一個相當新的版本,所以沒有那麼多用途。但我們需要它。將打開一個頁面,您可以在其中獲取各種包管理器的此依賴項的代碼,或者您可以直接下載它。但由於我們想使用 Maven 連接它,我們將在 Maven 選項卡上選擇代碼。我們複製並粘貼到 POM 文件的依賴項部分。如果您在IDEA的右下角收到詢問是否要啟用自動導入的通知,請繼續並同意它。如果您不小心拒絕了,請轉到“設置”並手動打開自動導入:設置 (Ctrl + Alt + S) -> Build, Execution, Deployment -> Maven -> Importing和該項目的 IDEA 配置文件同步。按照相同的原則,我們將找到並連接JavaServer Pages 2.3(搜索“JSP”)。由於我們已經啟動了 Maven,所以我們只需告訴它我們的源文件遵循 Java 8 語法,並且我們需要將它們編譯成該版本的字節碼。完成所有這些步驟後,我們的pom.xml將如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cc.codegym.info.fatfaggy</groupId>
    <artifactId>my-super-project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compile.source>1.8</maven.compile.source>
        <maven.compile.target>1.8</maven.compile.target>
    </properties>

    <dependencies>
        <!-- Servlet API 4.0 for tomcat 9 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- JavaServer Pages API 2.3 for tomcat 9 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

將我們的servlets變成真正的servlets

目前,我們創建的這對 servlet 實際上是普通的類。它們沒有任何功能。但現在我們已經將Servlet API連接到我們的項目,因此我們可以使用它的類。要使我們的servlets“真實”,我們需要做的就是讓它們繼承HttpServlet類。

映射或標記

現在最好以某種方式告訴Tomcat對 / add地址的請求由我們的AddServlet處理,而對 / list地址的請求由ListServlet處理。這個過程稱為映射(標記)。 這是在web.xml中使用相同的原則完成的:
  • 首先,描述 servlet(提供一些名稱並指定類本身的路徑);
  • 然後將這個 servlet 綁定到一個特定的地址(指定我們剛剛給它的 servlet 的名稱,並指定其請求應該發送到這個 servlet 的地址)。
描述小服務程序:

<servlet>
    <servlet-name>add</servlet-name>
    <servlet-class>app.servlets.AddServlet</servlet-class>
</servlet>
現在將其綁定到地址:

<servlet-mapping>
    <servlet-name>add</servlet-name>
    <url-pattern>/add</url-pattern>
</servlet-mapping>
如您所見,servlet-name 在這兩種情況下都是相同的。結果,Tomcat知道如果接收到 /add 請求,則必須將其發送到 app.servlets.AddServlet。我們對第二個 servlet 做同樣的事情。最後我們的web.xml大概有以下內容:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- add servlet -->
    <servlet>
        <servlet-name>add</servlet-name>
        <servlet-class>app.servlets.AddServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>add</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>

    <!-- list servlet -->
    <servlet>
        <servlet-name>list</servlet-name>
        <servlet-class>app.servlets.ListServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>list</servlet-name>
        <url-pattern>/list</url-pattern>
    </servlet-mapping>
</web-app>
順便說一下,我們沒有為主頁 (/) 創建標記。事實上,在這種情況下我們不需要它。我們的主頁是一個簡單的 HTML 文件,只顯示兩個按鈕。它沒有動態內容,所以我們不需要為來自/ 的請求創建一個單獨的 servlet ,它只會將執行轉發給一些JSP(也必須創建)來為我們繪製兩個按鈕。我們不需要這個。靜態頁面適合我們。當 Tomcat 收到一個請求時,它會檢查是否有一個 servlet 可以處理該地址的請求,然後會看到這個地址實際上已經包含了準備好的 HTML文件,它將提供。我們可以再次運行我們的應用程序(重新啟動服務器或重新部署它 - 無論您喜歡什麼)並確保主頁已呈現,沒有任何問題,並且在我們單擊按鈕時發生轉換(儘管我們再次收到錯誤)。順便說一句,之前我們得到 404 錯誤,現在我們得到 405。這意味著映射有效並且找到了 servlet,但是它們沒有合適的方法來處理請求。

簡短的題外話:“幕後”發生了什麼?

您可能已經考慮過我們的應用程序如何在 Tomcat 中運行。那裡發生了什麼?main() 方法在哪裡?只要您在瀏覽器中訪問 localhost:8080,瀏覽器就會使用 HTTP 協議向該地址發送請求。我希望您已經意識到有許多不同類型的請求,最流行的是 GET 和 POST。每個請求都應該得到答复。GET 請求預計會收到返回給瀏覽器的現成 HTML 代碼的響應。然後瀏覽器將替換所有漂亮的字母、按鈕和表單的代碼。POST 請求更有趣一些,因為它還攜帶了一些信息。例如,假設您在網站的註冊或登錄表單中輸入憑據,然後單擊“發送”。這會導致將包含您的個人信息的 POST 請求發送到服務器。服務器接收此信息,對其進行處理並返回一些響應(例如,包含您的個人資料的 HTML 頁面)。它們之間的主要區別在於 GET 請求僅用於從服務器檢索數據,而 POST 請求攜帶一些信息(並且服務器上的數據可以更改)。例如,當您將圖片上傳到服務器時,它會在 POST 請求中攜帶到服務器,然後服務器將其添加到數據庫中,即發生更改。現在回到 Tomcat。當它收到來自客戶端的請求時,它會查看地址。它檢查是否有合適的 servlet 來處理對該地址的請求(或可以立即返回的可用資源)。如果找不到要返回的東西,然後它以 404 錯誤而不是 HTML 頁面作為響應。但是,如果它在那個地址找到一個合適的 servlet,它就會查看請求類型(GET、POST 或其他)並詢問 servlet 是否有可以處理此類查詢的方法。如果 servlet 說它不知道如何處理這種類型,那麼Tomcat返回 405 代碼。這正是我們項目中發生的事情。但是如果找到合適的 servlet,並且它有合適的方法,Tomcat就會創建一個 servlet 對象,在新線程上啟動它(讓它自己運行),Tomcat 繼續自己的工作,接受和發送請求。此外,Tomcat 還創建了兩個對象:一個 HttpServletRequest(我將簡稱為“請求”)和一個 HttpServletResponse(我將其稱為“響應”)。它將從客戶端請求中接收到的所有數據放入第一個對像中,因此可以從中提取所有數據。然後在所有這一切之後,它將這兩個對像傳遞給在單獨線程上啟動的 servlet 的適當方法。一旦 servlet 完成其工作並準備好發送給客戶端的響應,它就會向 Tomcat 揮動旗幟,說“我完成了。一切都準備好了”。Tomcat 收到響應並將其發送給客戶端。這讓 Tomcat 接收請求並發送響應,不會分心,所有工作都由運行在不同線程上的 servlet 完成。這意味著當我們編寫 servlet 代碼時,我們確定將執行什麼工作。你可以認為 main() 方法位於 Tomcat 本身內部(是的,它是用 Java 編寫的),當我們“啟動”Tomcat 時,main() 方法就會啟動。 使用 servlet 和 JSP 創建簡單的 Web 應用程序(第 1 部分)- 2

使用 servlet 捕獲 GET 方法並發送超級簡單的響應

目前,我們的 servlet 沒有合適的方法 (GET),因此 Tomcat 返回 405 錯誤。讓我們創造它們!我們的 servlet 繼承的 HttpServlet 類聲明了各種方法。要將特定代碼分配給方法,我們只需覆蓋它們即可。在這種情況下,我們需要覆蓋doGet()兩個 servlet 中的方法。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
如您所見,此方法有兩個參數:req(請求)和 resp(響應)。這些正是 Tomcat 在調用 servlet 中的適當方法時為我們創建和填充的對象。首先,我們將創建最簡單的響應。為此,我們將獲取 resp 對象並從中獲取 PrintWriter 對象。這種類型的對像用於編寫響應。我們將使用它來輸出一個簡單的字符串。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter writer = resp.getWriter();
    writer.println("GET method from AddServlet");
}
我們將在 ListServlet 中做類似的事情,然後我們將重新啟動我們的服務器。如您所見,一切正常!當您單擊按鈕時,您將獲得包含我們使用 PrintWriter“編寫”的文本的頁面。但是我們準備生成帶有響應的頁面的 JSP 文件沒有被使用。那隻是因為他們從未被執行過。我們的 servlet 自己創建響應並完成運行,向 Tomcat 發出信號,表明它已準備好響應客戶端。Tomcat 只是獲取響應並將其發送回客戶端。 讓我們將控制權從 servlet 傳遞給 JSP 文件。我們將更改方法的代碼如下:
  • 我們從請求對像中獲取一個請求調度器對象,並將它傳遞給我們要將控制轉移到的 JSP 頁面的地址;
  • 我們使用此對象將控制轉移到指定的 JSP 頁面,不要忘記傳遞我們從 Tomcat 接收到的請求和響應對象。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
    requestDispatcher.forward(req, resp);
}
在JSP頁面的body標籤中,可以添加一些東西,這樣我們就可以清楚的看到顯示的是哪個頁面。完成後,重新啟動服務器並檢查。我們單擊主頁上的按鈕,頁面打開,這意味著請求被發送到 servlet。然後將控制權傳遞給現在正在呈現的 JSP 頁面。目前為止就這樣了。在本文的下一部分中,我們將研究應用程序的功能。