手寫一個Tomcat
阿新 • • 發佈:2018-12-20
(參考了公眾號:java架構沉思錄中的文章:教你寫一個迷你版的toncat 原文:https://www.jianshu.com/p/dcelee01fb90) 作為一個java學習的起步者,對tomcat的認識還是有很多的欠缺,在無意中發現了這篇文章,便在自己的環境下嘗試搭建,收穫良多: 分以下幾個步驟: (1)提供Socket服務 (2)進行請求的轉發 (3)把請求和響應封裝成request/response 程式碼實現如下: 1、工程截圖: 2、封裝請求物件:通過輸入流,對HTTP協議進行解析,拿到了HTTP請求頭的方法和URL:
/** * @author wangjie * @version 2018/11/9 * 封裝請求物件 * 通過輸入流,對http協議進行解析,拿到http請求頭的方法和url */ public class MyRequest { private String url; private String method; public MyRequest(InputStream inputStream) throws IOException{ String httpRequest =""; byte[] httpRequestBytes =new byte[1024]; int length =0; if((length=inputStream.read(httpRequestBytes)) >0){ httpRequest=new String(httpRequestBytes,0,length); } String httpHead = httpRequest.split("\n")[0]; url=httpHead.split("\\s")[1]; method=httpHead.split("\\s")[0]; System.out.println(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; } }
3、封裝響應物件:基於HTTP協議的格式進行輸出寫入。
/** * @author wangjie * @version 2018/11/9 * 封裝響應物件 * 基於HTTP協議的格式進行輸出寫入。 */ public class MyResponse { private OutputStream outputStream; public MyResponse(OutputStream outputStream){ this.outputStream = outputStream; } public void write(String content)throws IOException { StringBuffer httpResponse = new StringBuffer(); httpResponse.append("HTTP/1.1 200 OK\n") .append("Content-Type: text/html\n") .append("\r\n") .append("<html><body>") .append(content) .append("</body></html>"); outputStream.write(httpResponse.toString().getBytes()); outputStream.close(); } }
4、servlet請求處理基類:Tomcat是滿足Servlet規範的容器,所以Tomcat需要提供API:doGet/doPost/service。
/** * @author wangjie * @version 2018/11/9 * Servlet請求處理基類 */ public abstract class MyServlet { public abstract void doGet(MyRequest myRequest,MyResponse myResponse); public abstract void doPost(MyRequest myRequest,MyResponse myResponse); public void service(MyRequest myRequest,MyResponse myResponse){ if(myRequest.getMethod().equalsIgnoreCase("POST")){ doPost(myRequest,myResponse); }else if(myRequest.getMethod().equalsIgnoreCase("GET")){ doGet(myRequest,myResponse); } } }
5、Servlet實現類:提供2個實現類,用於測試。
/**
* @author wangjie
* @version 2018/11/9
* servlet實現類
*/
public class FindGirlServlet extends MyServlet{
@Override
public void doGet(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("get gril....");
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("post girl...");
} catch (IOException e){
e.printStackTrace();
}
}
}
/**
* @author wangjie
* @version 2018/11/9
*/
public class HelloWorldServlet extends MyServlet {
@Override
public void doGet(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("get world...");
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("post world...");
}catch (IOException e){
e.printStackTrace();
}
}
}
6、Servlet配置:對比之前在web開發中,會在web.xml中通過和指定哪個URL交給哪個servlet來處理。
/**
* @author wangjie
* @version 2018/11/9
* servlet配置
*/
public class ServletMapping {
private String servletName;
private String url;
private String clazz;
public ServletMapping(String servletName, String url, String clazz){
this.servletName=servletName;
this.url=url;
this.clazz=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;
}
}
/**
* @author wangjie
* @version 2018/11/9
*/
public class ServletMappingConfig {
public static List<ServletMapping> servletMappingList =new ArrayList<>();
//制定哪個URL交給哪個servlet來處理
static{
servletMappingList.add(new ServletMapping("findGirl","/girl","wj.mytomcat.FindGirlServlet"));
servletMappingList.add(new ServletMapping("helloWorld","/world","wj.mytomcat.HelloWorldServlet"));
}
}
7、啟動類: tomcat的處理流程:把URL對應處理的Servlet關係形成,解析HTTP協議,封裝請求/響應物件,利用反射例項化具體的Servlet進行處理。
/**
* @author wangjie
* @version 2018/11/9
* tomcat啟動類
*/
public class MyTomcat {
private int port=8088;
private Map<String,String> urlServletMap =new HashMap<String,String>();
public MyTomcat(int port){
this.port=port;
}
public void start(){
// 初始化URL與對應處理的servlet的關係
initServletMapping();
ServerSocket serverSocket=null;
try{
serverSocket = new ServerSocket(port);
System.out.println("MyTomcat is start...");
while(true){
Socket socket= serverSocket.accept();
InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();
MyRequest myRequest= new MyRequest(inputStream);
MyResponse myResponse =new MyResponse(outputStream);
// 請求分發
dispatch(myRequest,myResponse);
socket.close();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if (null != serverSocket){
try{
serverSocket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
private void initServletMapping(){
for(ServletMapping servletMapping:ServletMappingConfig.servletMappingList){
urlServletMap.put(servletMapping.getUrl(),servletMapping.getClazz());
}
}
public void dispatch(MyRequest myRequest,MyResponse myResponse){
String clazz =urlServletMap.get(myRequest.getUrl());
//反射
try{
Class<MyServlet> myServletClass =(Class<MyServlet>) Class.forName(clazz);
MyServlet myServlet= myServletClass.newInstance();
myServlet.service(myRequest,myResponse);
}catch (ClassNotFoundException e){
e.printStackTrace();
}catch (InstantiationException e){
e.printStackTrace();
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
public static void main(String[] args){
new MyTomcat(8088).start();
}
}
8、測試: 執行專案後,在瀏覽器輸入:localhost:8088/girl 在瀏覽器輸入:localhost:8088/world 實踐完成。