Java 網路程式設計----初探Servlet

王若伊_恩赐解脱發表於2024-11-15

Jave Web是java面向web開發的相關技術,他是相關技術的統稱,並不是指某一個單一的技術。
在我之前的部落格中(Java網路程式設計----透過實現簡易聊天工具來聊聊BIO模型 https://www.cnblogs.com/jilodream/p/17405923.htm),就已經寫到過java可以作為一個伺服器(如TCP/UDP),接收外部的請求。如使用TCP監聽埠,然後直接用web頁面請求該埠,那麼伺服器就會接收到相關的響應,接著做好業務處理,返回響應的請求即可。但是整個的業務流程太繁瑣了。我們不但要處理業務流程,還要控制請求會話,還要控制各種業務分支的處理,顯然這不是我們想要的。
於是聰明的開發者很快想到了-----解耦,業務人員只要編寫相關的業務即可,不需要關心繁瑣的網路細節處理,因此就誕生了servlet。開發人員只要實現servlet,而servlet和不同的路徑繫結。web請求後,由系統直接轉發到各自的Servlet,並由Servlet來處理相關業務即可。
那什麼是servlet呢?servlet 是Server Applet(伺服器應用程式)的簡寫,從名字我們就可以看出它是專門用來編寫伺服器端的應用程式。我們通常用它來處理伺服器中http請求的處理和響應。
它是一項很古老的技術,隨java誕生之初就已經問世,很多人甚至都不知Servlet是做什麼。那麼問題來了,我們為什麼還要學習和掌握Servlet呢?這主要是由於Servlet是javaEE的重要組成,是java web開發的重要基石。我們現在專案中常用到的Jsp、Springmvc、Springboot等框架,在處理網路請求的核心技術,仍然是Servlet。我們雖然不需要再繼續直面Servlet或更底層的技術進行開發,但是Servlet究竟是什麼樣的,如何執行,以及再新技術中承擔什麼樣的角色,這個卻是我們想要熟悉底層原理所必須要掌握的。

話不多說,想要使用Servlet,我們需要做兩步:

1、編寫Servlet相關業務程式碼
2、將業務程式碼打包放置在Tomcat中,由Tomcat來載入這些Servlet

第一步,試著來編寫一個Servlet
我們首先透過IDEA 新建一個web專案,此處我們選擇採用maven部署,搭建好之後專案整體的結構就如下面的檔案層級樹一樣

E:.
├─.idea
├─.smarttomcat
│  └─PureJaveServlet
│      └─conf
└─src
    └─main
        ├─java
        │  └─org
        │      └─example
        ├─resources
        └─webapp

其中:

resources 一般是我們填寫的資源資訊,如圖片,業務配置檔案等,
webapp則會放置html、css等渲染檔案,還會放一些web元件(Servlet、Filter)的配置資訊。我們這裡只要知道作用即可。
src/main/java則是我們的業務程式碼。值得注意的是,我們要引入Servlet網路程式設計的相關依賴包,才能進行相關的web開發。預設的java SE是不包含這些開發包的。
在Servlet4.0之前,我們只使用javax相關的包,並且未來對接的是Tomcat 9.x及以下版本。(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
在Servlet5.0之後,我們只使用javax相關的包,並且未來對接的是Tomcat 10.x及以後版本。
這裡我們使用高版本來學習,即jakarta版本,未來tomcat也需要使用高版本。

接著編寫POM檔案:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 5 <modelVersion>4.0.0</modelVersion>
 6 
 7 <groupId>org.example</groupId>
 8 <artifactId>PureJaveServlet</artifactId>
 9 <version>1.0-SNAPSHOT</version>
10 <name>PureJaveServlet</name>
11 <packaging>war</packaging>
12 
13 <properties>
14     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15     <maven.compiler.target>11</maven.compiler.target>
16     <maven.compiler.source>11</maven.compiler.source>
17 </properties>
18 
19 <dependencies>
20     <dependency>
21         <groupId>jakarta.servlet</groupId>
22         <artifactId>jakarta.servlet-api</artifactId>
23         <version>5.0.0</version>
24         <scope>provided</scope>
25     </dependency>
26 
27     <dependency>
28         <groupId>org.projectlombok</groupId>
29         <artifactId>lombok</artifactId>
30         <version>1.18.30</version>
31     </dependency>
32     <dependency>
33         <groupId>org.apache.commons</groupId>
34         <artifactId>commons-lang3</artifactId>
35         <version>3.13.0</version>
36     </dependency>
37     <!-- http客戶端 -->
38     <!-- fastjson -->
39     <dependency>
40         <groupId>com.alibaba</groupId>
41         <artifactId>fastjson</artifactId>
42         <version>1.2.83</version>
43     </dependency>
44 
45 
46     <dependency>
47         <groupId>com.fasterxml.jackson.core</groupId>
48         <artifactId>jackson-databind</artifactId>
49         <version>2.10.0</version>
50     </dependency>
51     <dependency>
52         <groupId>io.pebbletemplates</groupId>
53         <artifactId>pebble</artifactId>
54         <version>3.1.6</version>
55     </dependency>
56     <dependency>
57         <groupId>org.apache.maven.plugins</groupId>
58         <artifactId>maven-compiler-plugin</artifactId>
59         <version>3.10.1</version>
60     </dependency>
61 
62 </dependencies>
63 
64 <build>
65     <plugins>
66         <plugin>
67             <groupId>org.apache.maven.plugins</groupId>
68             <artifactId>maven-war-plugin</artifactId>
69             <version>3.3.2</version>
70         </plugin>
71         <plugin>
72             <groupId>org.apache.maven.plugins</groupId>
73             <artifactId>maven-compiler-plugin</artifactId>
74             <configuration>
75                 <compilerArgs>
76                     <arg>-parameters</arg>
77                 </compilerArgs>
78             </configuration>
79         </plugin>
80     </plugins>
81 </build>
82 </project>

Servlet類

 1 package org.example;
 2 
 3 
 4 import jakarta.servlet.ServletException;
 5 import jakarta.servlet.annotation.WebServlet;
 6 import jakarta.servlet.http.HttpServlet;
 7 import jakarta.servlet.http.HttpServletRequest;
 8 import jakarta.servlet.http.HttpServletResponse;
 9 
10 import java.io.IOException;
11 import java.io.PrintWriter;
12 
13 
14 @WebServlet(urlPatterns = "/nihao")
15 public class HiServlet extends HttpServlet {
16     @Override
17     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws  IOException {
18         String name = req.getParameter("name");
19         resp.setContentType("text/html");
20         PrintWriter out = resp.getWriter();
21         out.println("<html><body>");
22         out.println(String.format("<h1>Hello, %s </h1>", name));
23         out.println("</body></html>");
24         out.flush();
25     }
26 }

 1 package org.example;
 2 
 3 import jakarta.servlet.annotation.WebServlet;
 4 import jakarta.servlet.http.HttpServlet;
 5 import jakarta.servlet.http.HttpServletRequest;
 6 import jakarta.servlet.http.HttpServletResponse;
 7 
 8 import java.io.IOException;
 9 import java.io.PrintWriter;
10 
11 /**
12  * @discription
13  */
14 @WebServlet(urlPatterns = "/bye")
15 public class ByeServlet extends HttpServlet {
16     @Override
17     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
18         String name = req.getParameter("name");
19         resp.setContentType("text/html");
20         PrintWriter out = resp.getWriter();
21         out.println("<html><body>");
22         out.println(String.format("<h1>bye bye, %s </h1>", name));
23         out.println("</body></html>");
24         out.flush();
25     }
26 }

程式碼部分就結束了,我們觀察程式碼可以發現兩點:
1、所有的Sevlet都要繼承自HttpServlet。HttpServlet是一個抽象類,我們需要複寫抽象類中的抽象方法,以保證未來Tomcat等web伺服器在載入Servlet時,可以按照統一的規範查詢,執行。
我們在Servlet中重寫了doGet() 方法,表示處理該servlet路徑下的get請求,同理還可以重寫doPost doDelete doPut等方法,來處理對應的請求型別。
這裡我們很簡單,直接返回一段響應的html。
2、我們並沒有寫main方法,而是在Pom中標記我們的工程需要打包成一個war包。
第二步,配置tomcat
沒有main方法,我們如何啟動我們的java程式呢?我們通常是將其配置到tomcat的指定路徑中,啟動tomcat後,tomcat會載入war包中servlet的相關類,進行處理。
因此我們會將tomcat這樣的web伺服器稱之為Servlet容器。
我們首先從tomcat官網上(https://tomcat.apache.org/whichversion.html)下載一個與我們對應的servlet版本匹配的tomcat版本。

下載到本地之後解壓即可。
接著我們為了方便在IDEA中下載一個smart tomcat的元件,將該元件關聯好servlet程式碼和tomcat伺服器即可。
關鍵配置如下:
Tomcat server: 選擇我們下載好的tomcat伺服器,如果沒有下拉選項就選擇"Congure..."手動加一下。
Deployment dirctory:部署資料夾,(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )該配置指向前文專案結構樹種的webapp。
Use classpath of module:選擇當前專案
Context path:選擇上下文路徑(其實就是url的字首路徑),按照url規則隨便填,我這裡叫填的是/biubiubiu
server port:Servlet的伺服器埠,預設填8080
admin port:tomcat的管理埠 預設填8005,一般就是用於停掉tomcat(其實一般也不用)。
之後我們透過IDEA:拉起tomcat,載入servlet相關類和資源。

命令列輸入如下:

....
15-Nov-2024 10:34:38.099 資訊 [main] org.apache.coyote.AbstractProtocol.start 開始協議處理控制代碼["http-nio-8080"] 15-Nov-2024 10:34:39.831 資訊 [main] org.apache.catalina.startup.Catalina.start [4858]毫秒後伺服器啟動 http://localhost:8080/biubiubiu

執行效果如下:

有人會覺得透過下載並配置tomcat有點麻煩,我們如果想debug程式碼的話,就更麻煩了,有沒有簡單點的辦法:
其實除了下載tomcat,我們還可以透過程式碼的形式直接拉起tomcat。思路如下:
首先透過maven載入對應tomcat依賴,然後在main方法中建立tomcat例項,並且指定tomcat所需要的配置資訊,如資源和class路徑。然後透過start()方法啟動tomcat例項就可以了。
程式碼如下:

新搭建一個java web專案,Servlet類和工程結構我們保持不變還是和原來一樣

POM檔案

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.example</groupId>
 8     <artifactId>demotom</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10     <name>demotom</name>
11     <packaging>war</packaging>
12 
13     <properties>
14         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15         <maven.compiler.target>11</maven.compiler.target>
16         <maven.compiler.source>11</maven.compiler.source>
17         <tomcat.version>10.0.0</tomcat.version>
18     </properties>
19 
20     <dependencies>
21 
22 
23         <dependency>
24             <groupId>org.apache.tomcat.embed</groupId>
25             <artifactId>tomcat-embed-core</artifactId>
26             <version>${tomcat.version}</version>
27             <scope>provided</scope>
28         </dependency>
29         <dependency>
30             <groupId>org.apache.tomcat.embed</groupId>
31             <artifactId>tomcat-embed-jasper</artifactId>
32             <version>${tomcat.version}</version>
33             <scope>provided</scope>
34         </dependency>
35     </dependencies>
36 
37     <build>
38         <plugins>
39             <plugin>
40                 <groupId>org.apache.maven.plugins</groupId>
41                 <artifactId>maven-war-plugin</artifactId>
42                 <version>3.3.2</version>
43             </plugin>
44         </plugins>
45     </build>
46 </project>

主類:

 1 package com.example.demotom;
 2 
 3 import org.apache.catalina.Context;
 4 import org.apache.catalina.LifecycleException;
 5 import org.apache.catalina.WebResourceRoot;
 6 import org.apache.catalina.startup.Tomcat;
 7 import org.apache.catalina.webresources.DirResourceSet;
 8 import org.apache.catalina.webresources.StandardRoot;
 9 
10 import java.io.File;
11 
12 /**
13  * @discription
14  */
15 public class TomMain {
16     public static void main(String[] args) throws LifecycleException {
17         Tomcat tomcat = new Tomcat();
18         tomcat.setPort(Integer.getInteger("port", 8080));
19         tomcat.getConnector();
20         String docBase = new File("src/main/webapp").getAbsolutePath();
21         Context ctx = tomcat.addContext("", docBase);
22         WebResourceRoot resources = new StandardRoot(ctx);
23         String base = new File("target/classes").getAbsolutePath();
24         resources.addJarResources(new DirResourceSet(resources, "/WEB-INF/classes", base, "/"));
25         ctx.setResources(resources);
26         tomcat.start();
27         tomcat.getServer().await();
28     }
29 }

啟動後控制檯輸出如下,我們可以看到8080埠已經被監聽:

"C:\Program Files\Java\jdk-11\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:51854,suspend=y,server=n -Dfile.encoding=UTF-8 -classpath ....
Connected to the target VM, address: '127.0.0.1:51854', transport: 'socket'
11月 15, 2024 10:47:54 上午 org.apache.coyote.AbstractProtocol init
資訊: Initializing ProtocolHandler ["http-nio-8080"]
11月 15, 2024 10:47:54 上午 org.apache.catalina.core.StandardService startInternal
資訊: Starting service [Tomcat]
11月 15, 2024 10:47:54 上午 org.apache.catalina.core.StandardEngine startInternal
資訊: Starting Servlet engine: [Apache Tomcat/10.0.0]
11月 15, 2024 10:47:56 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
警告: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [1,753] milliseconds.
11月 15, 2024 10:47:56 上午 org.apache.coyote.AbstractProtocol start
資訊: Starting ProtocolHandler ["http-nio-8080"]

相關文章