|
今天我們來一步一步實現(xiàn)從0開始手寫Tomcat。 首先,我們要知道Tomcat是什么,能做什么。 Tomcat是一個Web網(wǎng)絡(luò)應(yīng)用程序,可以接收請求,并且可以處理請求。接收請求意味著必須要有一個端口,Tomcat默認(rèn)http端口是8080,請求過來后由socket網(wǎng)絡(luò)處理,請求最終需要交給線程處理,根據(jù)URL調(diào)用servlet,之后返回響應(yīng)內(nèi)容。 那么要實現(xiàn)Tomcat的功能,首先第一步我們要實現(xiàn)Socket編程。 Socket實際上做了什么呢? Socket調(diào)用操作系統(tǒng)的SocketAPI來實現(xiàn)網(wǎng)絡(luò)處理。所謂網(wǎng)絡(luò)編程,就是對外開放接口,讓我們的程序可以與外部建立連接。 底層Socket API函數(shù)定義
了解完socket編程后,我們先來完成Socket網(wǎng)絡(luò)處理部分,如下: private static ExecutorService threadPool = Executors.newCachedThreadPool(); public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8080); System.out.println('tomcat 服務(wù)器啟動成功'); while (!serverSocket.isClosed()) { //獲取Socket連接 Socket request = serverSocket.accept(); threadPool.execute(() -> { try (InputStream is = request.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)) ){ System.out.println('收到請求:'); String msg = null; while ((msg = reader.readLine()) != null) { if (msg.length() == 0) { break; } System.out.println(msg); } System.out.println('---------收到請求'); } catch (IOException e) { e.printStackTrace(); } finally { try { if (request != null) { request.close(); } } catch (IOException e) { e.printStackTrace(); } } }); } if (serverSocket != null) { serverSocket.close(); } }完成Socket網(wǎng)絡(luò)編程,我們啟動服務(wù)后,通過telnet可以發(fā)現(xiàn)8080端口已啟動,但是我們通過瀏覽器訪問http://localhost:8080 卻發(fā)現(xiàn)瀏覽器顯示“無法訪問該頁面”。這是為什么呢? 我們的目標(biāo)是要實現(xiàn)瀏覽器和Java Web服務(wù)器Tomcat實現(xiàn)交互,那么Java Web服務(wù)器Tomcat如何與瀏覽器交互呢? Tomcat和瀏覽器之間要交互,需要約定一個協(xié)議,這樣才能正常交互。 Http協(xié)議 --- 請求 Http協(xié)議 --- 響應(yīng) 為了實現(xiàn)Java Web服務(wù)器Tomca與瀏覽器之間能正常交互,我們需要加上一段請求響應(yīng)代碼,完整代碼如下:
這時候,我們啟動服務(wù)后,再通過瀏覽器去訪問http://localhost:8080/,我們會發(fā)現(xiàn)瀏覽器返回了“Hello World”。 接下來,我們要實現(xiàn)根據(jù)請求URL執(zhí)行相應(yīng)的servlet方法。 例如:“GET /servlet-demo-1.0.0/index HTTP/1.1”,我們要根據(jù)這樣一個請求去找到對應(yīng)的項目及調(diào)用相應(yīng)的servlet。 首先我們要知道有哪幾個項目,請求的項目是哪個,對應(yīng)的servlet是哪個。通過請求路徑,我們可以知道請求的項目名稱是“servlet-demo-1.0.0”,請求的servlet路徑是 /index 。我們可以根據(jù)項目名稱查找到對應(yīng)的項目,讀取項目的web.xml,從web.xml中獲取到Servlet對應(yīng)的class,也可以獲取到ServletMapping設(shè)置的路徑來做請求過濾,也可以獲取其他信息來做相應(yīng)的一些操作。 這里有一個問題,獲取到的class文件可以執(zhí)行用來調(diào)用Servlet嗎? 答案是肯定不行,了解JVM的應(yīng)該都知道,我們需要先將class文件加載到JVM,然后才能正常調(diào)用相應(yīng)的方法。 項目類資源加載
通過以下代碼,我們可以實現(xiàn)對項目類加載,獲取到Servlet實例。 //每個項目,類加載器,去加載置頂位置的class信息URL classUrl = new URL('file:' + projectPath + '\\WEB-INF\\classes\\');URLClassLoader servletClassLoader = new URLClassLoader(new URL[] {classUrl});//1、 加載到JVMClass<?> servletClass = servletClassLoader.loadClass(servletClassName);//2、 實例化Servlet servlet = (Servlet) servletClass.newInstance();我們先來回顧一下Servlet的生命周期是怎樣的,如下所示:
在我們這個模擬例子中,我們就不實現(xiàn)init()和destory()了,我們就來調(diào)用service()方法處理客戶端請求。javax.servlet.Servlet的service()方法是有兩個參數(shù)的 我們在應(yīng)用開發(fā)中常用的HttpServletRequest和HttpServletResponse,實際上是我們的Java Web服務(wù)器已經(jīng)幫我們實現(xiàn)了,這里我們也需要來實現(xiàn)我們的HttpServletRequest和HttpServletResponse,然后在調(diào)用service()方法就可以了。到這里,我們簡易版的Web服務(wù)器就算成功了。 代碼地址:https://github.com/biaotang/demo-tomcat |
|
|