摘要:項(xiàng)目結(jié)構(gòu)項(xiàng)目結(jié)構(gòu)如下實(shí)現(xiàn)細(xì)節(jié)創(chuàng)建對(duì)象首先創(chuàng)建自定義的請(qǐng)求類,其中定義與兩個(gè)屬性,表示請(qǐng)求的以及請(qǐng)求的方式。其構(gòu)造函數(shù)需要傳入一個(gè)輸入流,該輸入流通過客戶端的套接字對(duì)象得到。創(chuàng)建服務(wù)端套接字,并綁定某個(gè)端口。
緣起
用了那么久tomcat,突然覺得自己對(duì)它或許并沒有想象中的那么熟悉,所以趁著放假我研究了一下這只小貓咪,實(shí)現(xiàn)了自己的小tomcat,寫出這篇文章同大家一起分享!
照例附上github鏈接。
項(xiàng)目結(jié)構(gòu)如下:
首先創(chuàng)建自定義的請(qǐng)求類,其中定義url與method兩個(gè)屬性,表示請(qǐng)求的url以及請(qǐng)求的方式。
其構(gòu)造函數(shù)需要傳入一個(gè)輸入流,該輸入流通過客戶端的套接字對(duì)象得到。
輸入流中的內(nèi)容為瀏覽器傳入的http請(qǐng)求頭,格式如下:
GET /student HTTP/1.1 Host: localhost:8080 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: Hm_lvt_eaa22075ffedfde4dc734cdbc709273d=1549006558; _ga=GA1.1.777543965.1549006558
通過對(duì)以上內(nèi)容的分割與截取,我們可以得到該請(qǐng)求的url以及請(qǐng)求的方式。
package tomcat.dome; import java.io.IOException; import java.io.InputStream; //實(shí)現(xiàn)自己的請(qǐng)求類 public class MyRequest { //請(qǐng)求的url private String url; //請(qǐng)求的方法類型 private String method; //構(gòu)造函數(shù) 傳入一個(gè)輸入流 public MyRequest(InputStream inputStream) throws IOException { //用于存放http請(qǐng)求內(nèi)容的容器 StringBuilder httpRequest=new StringBuilder(); //用于從輸入流中讀取數(shù)據(jù)的字節(jié)數(shù)組 byte[]httpRequestByte=new byte[1024]; int length=0; //將輸入流中的內(nèi)容讀到字節(jié)數(shù)組中,并且對(duì)長(zhǎng)度進(jìn)行判斷 if((length=inputStream.read(httpRequestByte))>0) { //證明輸入流中有內(nèi)容,則將字節(jié)數(shù)組添加到容器中 httpRequest.append(new String(httpRequestByte,0,length)); } //將容器中的內(nèi)容打印出來 System.out.println("httpRequest = [ "+httpRequest+" ]"); //從httpRequest中獲取url,method存儲(chǔ)到myRequest中 String httpHead=httpRequest.toString().split(" ")[0]; url=httpHead.split("s")[1]; method=httpHead.split("s")[0]; System.out.println("MyRequests = [ "+this+" ]"); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }
創(chuàng)建自定義的響應(yīng)類,構(gòu)造函數(shù)需要傳入由客戶端的套接字獲取的輸出流。
定義其write方法,像瀏覽器寫出一個(gè)響應(yīng)頭,并且包含我們要寫出的內(nèi)容content。
package tomcat.dome; import java.io.IOException; import java.io.OutputStream; //實(shí)現(xiàn)自己的響應(yīng)類 public class MyResponse { //定義輸出流 private OutputStream outputStream; //構(gòu)造函數(shù) 傳入輸出流 public MyResponse(OutputStream outputStream) { this.outputStream=outputStream; } //創(chuàng)建寫出方法 public void write(String content)throws IOException{ //用來存放要寫出數(shù)據(jù)的容器 StringBuffer stringBuffer=new StringBuffer(); stringBuffer.append("HTTP/1.1 200 OK ") .append("Content-type:text/html ") .append(" ") .append("Hello World ") .append(content) .append(""); //轉(zhuǎn)換成字節(jié)數(shù)組 并進(jìn)行寫出 outputStream.write(stringBuffer.toString().getBytes()); //System.out.println("sss"); outputStream.close(); } }
由于我們自己寫一個(gè)Servlet的時(shí)候需要繼承HttpServlet,因此在這里首先定義了一個(gè)抽象類——MyServlet對(duì)象。
在其中定義了兩個(gè)需要子類實(shí)現(xiàn)的抽象方法doGet和doSet。
并且創(chuàng)建了一個(gè)service方法,通過對(duì)傳入的request對(duì)象的請(qǐng)求方式進(jìn)行判斷,確定調(diào)用的是doGet方法或是doPost方法。
package tomcat.dome; //寫一個(gè)抽象類作為servlet的父類 public abstract class MyServlet { //需要子類實(shí)現(xiàn)的抽象方法 protected abstract void doGet(MyRequest request,MyResponse response); protected abstract void doPost(MyRequest request,MyResponse response); //父類自己的方法 //父類的service方法對(duì)傳入的request以及response //的方法類型進(jìn)行判斷,由此調(diào)用doGet或doPost方法 public void service(MyRequest request,MyResponse response) throws NoSuchMethodException { if(request.getMethod().equalsIgnoreCase("POST")) { doPost(request, response); }else if(request.getMethod().equalsIgnoreCase("GET")) { doGet(request, response); }else { throw new NoSuchMethodException("not support"); } } }
這里我創(chuàng)建了兩個(gè)業(yè)務(wù)相關(guān)類:StudentServlet和TeacherServlet。
package tomcat.dome; import java.io.IOException; //實(shí)現(xiàn)自己業(yè)務(wù)相關(guān)的Servlet public class StudentServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的輸出流 寫出內(nèi)容 try { //System.out.println("!!!!!!!!!!!!!!!!!!"); response.write("I am a student."); //System.out.println("9999999999999999"); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的輸出流 寫出內(nèi)容 try { response.write("I am a student."); }catch(IOException e) { e.printStackTrace(); } } }
package tomcat.dome; import java.io.IOException; //實(shí)現(xiàn)自己業(yè)務(wù)相關(guān)的Servlet public class TeacherServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的輸出流 寫出內(nèi)容 try { response.write("I am a teacher."); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的輸出流 寫出內(nèi)容 try { response.write("I am a teacher."); }catch(IOException e) { e.printStackTrace(); } } }
該結(jié)構(gòu)實(shí)現(xiàn)的是請(qǐng)求的url與具體的Servlet之間的關(guān)系映射。
package tomcat.dome; //請(qǐng)求url與項(xiàng)目中的servlet的映射關(guān)系 public class ServletMapping { //servlet的名字 private String servletName; //請(qǐng)求的url private String url; //servlet類 private String clazz; public String getServletName() { return servletName; } public void setServletName(String servletName) { this.servletName = servletName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public ServletMapping(String servletName, String url, String clazz) { super(); this.servletName = servletName; this.url = url; this.clazz = clazz; } }
配置類中定義了一個(gè)列表,里面存儲(chǔ)著項(xiàng)目中的映射關(guān)系。
package tomcat.dome; import java.util.ArrayList; import java.util.List; //創(chuàng)建一個(gè)存儲(chǔ)有請(qǐng)求路徑與servlet的對(duì)應(yīng)關(guān)系的 映射關(guān)系配置類 public class ServletMappingConfig { //使用一個(gè)list類型 里面存儲(chǔ)的是映射關(guān)系類Mapping public static ListservletMappings=new ArrayList<>(16); //向其中添加映射關(guān)系 static { servletMappings.add(new ServletMapping("student","/student", "tomcat.dome.StudentServlet")); servletMappings.add(new ServletMapping("teacher","/teacher", "tomcat.dome.TeacherServlet")); } }
在服務(wù)端MyTomcat中主要做了如下幾件事情:
1)初始化請(qǐng)求的映射關(guān)系。
2)創(chuàng)建服務(wù)端套接字,并綁定某個(gè)端口。
3)進(jìn)入循環(huán),用戶接受客戶端的鏈接。
4)通過客戶端套接字創(chuàng)建request與response對(duì)象。
5)根據(jù)request對(duì)象的請(qǐng)求方式調(diào)用相應(yīng)的方法。
6)啟動(dòng)MyTomcat!
package tomcat.dome; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; //Tomcat服務(wù)器類 編寫對(duì)請(qǐng)求做分發(fā)處理的相關(guān)邏輯 public class MyTomcat { //端口號(hào) private int port=8080; //用于存放請(qǐng)求路徑與對(duì)應(yīng)的servlet類的請(qǐng)求映射關(guān)系的map //相應(yīng)的信息從配置類中獲取 private MapurlServletMap=new HashMap<>(16); //構(gòu)造方法 public MyTomcat(int port) { this.port=port; } //tomcat服務(wù)器的啟動(dòng)方法 public void start() { //初始化請(qǐng)求映射關(guān)系 initServletMapping(); //服務(wù)端的套接字 ServerSocket serverSocket=null; try { //創(chuàng)建綁定到某個(gè)端口的服務(wù)端套接字 serverSocket=new ServerSocket(port); System.out.println("MyTomcat begin start..."); //循環(huán) 用于接收客戶端 while(true) { //接收到的客戶端的套接字 Socket socket=serverSocket.accept(); //獲取客戶端的輸入輸出流 InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream(); //通過輸入輸出流創(chuàng)建請(qǐng)求與響應(yīng)對(duì)象 MyRequest request=new MyRequest(inputStream); MyResponse response=new MyResponse(outputStream); //根據(jù)請(qǐng)求對(duì)象的method分發(fā)請(qǐng)求 調(diào)用相應(yīng)的方法 dispatch(request, response); //關(guān)閉客戶端套接字 socket.close(); } } catch (IOException e) { e.printStackTrace(); } } //初始化請(qǐng)求映射關(guān)系,相關(guān)信息從配置類中獲取 private void initServletMapping() { for(ServletMapping servletMapping:ServletMappingConfig.servletMappings) { urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz()); } } //通過當(dāng)前的request以及response對(duì)象分發(fā)請(qǐng)求 private void dispatch(MyRequest request,MyResponse response) { //根據(jù)請(qǐng)求的url獲取對(duì)應(yīng)的servlet類的string String clazz=urlServletMap.get(request.getUrl()); //System.out.println("====="+clazz); try { //通過類的string將其轉(zhuǎn)化為對(duì)象 Class servletClass=Class.forName("tomcat.dome.StudentServlet"); //實(shí)例化一個(gè)對(duì)象 MyServlet myServlet=(MyServlet)servletClass.newInstance(); //調(diào)用父類方法,根據(jù)request的method對(duì)調(diào)用方法進(jìn)行判斷 //完成對(duì)myServlet中doGet與doPost方法的調(diào)用 myServlet.service(request, response); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } //main方法 直接啟動(dòng)tomcat服務(wù)器 public static void main(String[] args) { new MyTomcat(8080).start(); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/73638.html
摘要:算是心血來潮吧,想看看的源碼,順便學(xué)習(xí)一下。這里就簡(jiǎn)單的做點(diǎn)記錄如何用跟蹤查看源碼。當(dāng)然這里也提供書籍的源碼,可以下載下來對(duì)照書看。附上一張運(yùn)行成功的結(jié)果圖 算是心血來潮吧,想看看 Tomcat 的源碼,順便學(xué)習(xí)一下。 這里就簡(jiǎn)單的做點(diǎn)記錄--如何用 IDEA 跟蹤查看 Tomcat 源碼。 同時(shí),這里也推薦下 how-tomcat-works 這本書,雖然書中講的是 tomc...
摘要:接下來我們要配置這個(gè)的端口,這樣他們才能運(yùn)行時(shí)端口號(hào)不沖突。問題指明不同的端口號(hào)訪問也太蠢了吧的確很蠢,所以我們要慢慢過渡學(xué)習(xí)。接下來我們學(xué)習(xí)用來進(jìn)行反向代理。阿里云的部分有一些配置的具體過程。 一、在linux上部署運(yùn)行多個(gè)tomcat 1、以前的我們 雖然說是在linux上,但是windows上也是同樣的道理,只不過我們服務(wù)器都是選用linux罷了。 原先,自己有多個(gè)項(xiàng)目需要部署在...
閱讀 2638·2023-04-25 18:13
閱讀 870·2021-11-22 12:10
閱讀 3050·2021-11-22 11:57
閱讀 2203·2021-11-19 11:26
閱讀 2235·2021-09-22 15:40
閱讀 1523·2021-09-03 10:28
閱讀 2758·2019-08-30 15:53
閱讀 2012·2019-08-30 15:44