目錄
一、什么是Servlet
1.servlet的定義
2.servlet的結構
二、實現servlet的流程圖
三、具體實現代碼
1、server
?2.實體類request&response
3.HttpServlet抽象類
4.再定義三個servlet進行測試?
?
Tomcat原理(3)——靜&動態資源以及運行項目的基本流程-CSDN博客文章瀏覽閱讀414次,點贊2次,收藏2次。Tomcat原理(2)——注解及注解的實現-CSDN博客(1)注解一般用于對程序的說明,就像注釋一樣,但是區別是注釋是給人看的,但是注解是給程序看的。(2)讓編譯器進行編譯檢查的作用,比如下邊這個@Override注解是重寫的意思,子類重寫了父類的方法,但是改動了方法名,所以報錯。https://blog.csdn.net/2301_78566776/article/details/144508887?spm=1001.2014.3001.5502?我們在上一篇博客中已經了解到了靜動態資源和tomcat的基本流程
一、什么是Servlet
1.servlet的定義
????????Servlet,全稱為Java Servlet,是運行在Java服務器端的程序,它主要用于接收和響應來自客戶端基于HTTP協議的請求。Servlet可以看作是在服務器上運行的小程序,用于生成動態Web內容。
2.servlet的結構
這是一個servlet的基本結構。我們觀察可以發現:
- Servlet繼承了一個HttpServlet抽象類
- servlet文件包含兩個主要的方法:doGet和doPost
- 觀察這兩個方法的入參:一個是request 一個是response。
二、實現servlet的流程圖
1.我們首先需要構造一個Server服務器模擬,來接受HTTP請求,并且返回method(get/post)
2.創建request實體類和respons實體類,在request實體類中設置method和path變量
3.創建HttpServlet抽象類,在類里定義doGet和doPost兩個抽象方法,并且判斷所傳method為什么類型,進行選擇執行。
4.具體執行的doGet和doPost方法,要在具體的servlet中執行(找抽象類的具體實現方法)——即我們每一個servlet都要對doGet和doPost進行重寫。
如圖所示
?
三、具體實現代碼
引入
我們嘗試接收整個http請求的信息
package com.qcby.tomcat.webservlet;/* * 服務器端 tomcat ——》接收信息 * */import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket;public class SocketServer {public static void main(String[] args) throws IOException {run();}public static void run() throws IOException {ServerSocket serverSocket= new ServerSocket(8080);//端口的范圍:0~65535while (true){//等待客戶端連接Socket socket = serverSocket.accept();//阻塞監聽:程序會在這里卡住。只有監聽到客戶端的信息后才會向下執行//輸出客戶端給我們發來的程序InputStream inputStream=socket.getInputStream();//打開輸入流:接收輸入的信息int count=0;while (count==0){count=inputStream.available();}byte[] bytes=new byte[count];//01010101010100001101010 用字節數組接收inputStream.read(bytes);String context=new String(bytes);System.out.println(context);}} }
結果如圖
我們可以看到這個請求的全部信息。第一行的前兩個詞為其method/path,在一會的server文件中我們只要前兩個詞。
1、server
????????這個類中我們獲取到了http請求的method(get/post)和path(@WebServlet中的path)
獲取的方法是對所有請求信息進行切割。并且在最后我們將method和path放入了request中
package com.qcby.tomcat.socket;import com.qcby.tomcat.Request.Request;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {private static Request request=new Request();public static void main(String[] args) throws Exception {// 1.打開通信端口 tomcat:8080 3306 ---------》進行網絡通信ServerSocket serverSocket = new ServerSocket(8080);System.out.println("****************server start.....");//2.接受請求數據while (true) {Socket socket = serverSocket.accept(); //--------------------->注意:此時監聽網卡的是:主線程System.out.println("有客戶進行了鏈接");new Thread(() -> {//處理數據---------》數據的處理在于讀和寫try {handler(socket);} catch (Exception e) {e.printStackTrace();}}).start();}}public static void handler(Socket socket) throws Exception {//讀取請求的數據InputStream inputStream = socket.getInputStream();requestContext(inputStream);}public static void requestContext(InputStream inputStream) throws IOException {// 創建一個StringBuilder對象,用于構建請求的第一行StringBuilder sb = new StringBuilder();int context; // 用于存儲每次從輸入流中讀取的單個字節// 讀取輸入流直到遇到換行符(\n)或文件結束(-1)while ((context = inputStream.read()) != -1) {// 如果讀取到換行符,則停止讀取if (context == '\n') {break; // 遇到換行符,退出循環}// 將讀取到的字節轉換為字符,并添加到StringBuilder中sb.append((char) context);}// 從StringBuilder中獲取第一行字符串,并去除首尾空格String firstLine = sb.toString().trim();// 檢查第一行是否為空if (firstLine.isEmpty()) {// 如果為空,則打印提示信息System.out.println("你輸入了一個空請求");} else {// 如果不為空,則按空格分割第一行字符串為單詞數組String[] words = firstLine.split("\\s+");// 打印出請求方法和請求URI(通常是數組的前兩個元素)// 注意:這里沒有檢查數組長度,如果數組長度小于2,將會拋出ArrayIndexOutOfBoundsException// 在實際應用中,應該添加適當的錯誤處理或驗證邏輯String method=words[0];String path=words[2];System.out.println(words[0] + " " + words[1]);request.setMethod(method);request.setPath(path);}}
}
@WebServlet接口
package com.qcby.tomcat.webservlet;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Target(value = ElementType.TYPE) @Retention(value = RetentionPolicy.RUNTIME) public @interface WebServlet {String path() default ""; }
?
?2.實體類request&response
生成了get和set方法
生成了全參和無參的構造函數
package com.qcby.tomcat.Request;public class Request {private String path;private String method;public Request(String path, String method) {this.path = path;this.method = method;}public Request() {}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}
}
?在這里 response我并沒有實現
package com.qcby.tomcat.Response;public class Response {
}
3.HttpServlet抽象類
抽象類中既可以有抽象方法也可以有具體方法。
package com.qcby.tomcat.HttpServlet;import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;public abstract class HttpServlet {public void service(Request request, Response response){if(request.getMethod().equals("GET")){doGet(request,response);}else if (request.getMethod().equals("POST")){doPost(request,response);}else if (request.getMethod().equals("")){System.out.println("未獲取到方法類型");}}public abstract void doGet(Request request,Response response);public abstract void doPost(Request request,Response response);}
4.再定義三個servlet進行測試?
注意!每個servlet都有繼承HttpServlet,并且在每個servlet中都要重寫doGet和doPost
package com.qcby.tomcat.MyWeb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(path ="myFirstServlet")
public class MyFirstServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {}@Overridepublic void doPost(Request request, Response response) {}
}
package com.qcby.tomcat.MyWeb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(path ="mySecondServlet")
public class MySecondServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {}@Overridepublic void doPost(Request request, Response response) {}
}
?
package com.qcby.tomcat.MyWeb;import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;@WebServlet(path ="myThirdServlet")
public class MyThirdServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {}@Overridepublic void doPost(Request request, Response response) {}
}
我們會發現它已經很像Java的servlet文件模板了?
補充:
如何讀取一個軟件包,遍歷里面的servlet,返回其類名和注解的path值
package com.qcby.tomcat;import com.qcby.tomcat.webservlet.WebServlet;import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List;public class MyTomcat {public static void main(String[] args) {try {// 1. 掃描包路徑 (com.wzh.tomcat.myweb)String packageName = "com.qcby.tomcat.MyWeb";List<Class<?>> classes = getClasses(packageName);// 2. 遍歷所有類,檢查是否有@WebServlet注解for (Class<?> clazz : classes) {if (clazz.isAnnotationPresent(WebServlet.class)) {// 3. 獲取@WebServlet注解的值WebServlet webServlet = clazz.getAnnotation(WebServlet.class);System.out.println("類名: " + clazz.getName() + " | URL路徑: " + webServlet.path());}}} catch (Exception e) {e.printStackTrace();}}/*** 獲取指定包下的所有類** @param packageName 包名,例如 "com.wzh.tomcat.myweb"* @return 類對象列表* @throws Exception*/private static List<Class<?>> getClasses(String packageName) throws Exception {List<Class<?>> classes = new ArrayList<>();String path = packageName.replace('.', '/'); // 將包名轉換為文件路徑// 通過類加載器獲取包的資源路徑ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Enumeration<URL> resources = classLoader.getResources(path);while (resources.hasMoreElements()) {URL resource = resources.nextElement();File directory = new File(resource.toURI());// 掃描文件夾下的所有類文件if (directory.exists()) {for (File file : directory.listFiles()) {if (file.getName().endsWith(".class")) {// 獲取類的完整類名String className = packageName + "." + file.getName().replace(".class", "");classes.add(Class.forName(className));}}}}return classes;} }