原生jdk實現tomcat功能+對比servletapi和tomcat執行流程分析 --動態版

接上文:實現tomcat對於靜態資源的功能
本文增加動態功能以及對比servletapi,項目目錄大概就是這樣項目目錄html主要是靜態的上篇文章說的

package cn.wcy.mytomcat2;

import java.io.File;
import java.io.FileInputStream;
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.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import cn.wcy.mytomcat2.Servlet;

public class Server2 {
	//定義一個變量,存放服務端WebContent目錄的絕對路徑
	public static String WEB_ROOT=System.getProperty("user.dir")+"/"+"WebContent";
	//定義靜態變量,用於存放本次請求的靜態頁面名稱
	private static String url = "";
	//定義一個靜態類型map,存儲服務端conf.properties中的配置信息
	private static Map<String,String> map = new HashMap<String,String>();
	static {
		Properties prop = new Properties();
		try {
			prop.load(new FileInputStream(WEB_ROOT+"\\conf.properties"));
			Set set = prop.keySet();
			Iterator iterator = set.iterator();
			while(iterator.hasNext()) {
				String key = (String)iterator.next();
				String value = prop.getProperty(key);
				map.put(key,value);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) throws IOException {
		//創建ServerSocket,監聽本機的80端口,等待來自客戶端的請求
		ServerSocket ss = null;
		Socket socket = null;
		OutputStream os = null;
		InputStream is = null;
		try {
			ss = new ServerSocket(8080);
			while(true) {
				//獲取到客戶端對應的socket
				socket = ss.accept();
				//獲取到輸入流對象
				is = socket.getInputStream();
				//獲取到輸出流對象
				os = socket.getOutputStream();
				//獲取HTTP協議的請求部分 ,截取客戶端訪問的資源名稱,將這個資源名稱賦值給url
				parse(is);
				//判斷一下獲取到的url是靜態資源還是動態資源
				if(null!=url) {
					//如果有.就是有後綴就是說知道了靜態資源名
					if(url.indexOf(".")!=-1) {
						//發送靜態資源
						sendStaticResource(os);
					}else {
						sendDynamicResource(is,os);
					}
				}
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			if(null!=is) {
				is.close();
				is = null;
			}
			if(null!=os) {
				os.close();
				os = null;
			}
			if(null!=socket) {
				socket.close();
				socket = null;
			}
		}
	}
	//獲取HTTP協議的請求部分 ,截取客戶端訪問的資源名稱,將這個資源名稱賦值給url
	private static void parse(InputStream is) throws IOException {
		//定義一個變量,存放http協議請求部分數據
		StringBuffer content = new StringBuffer(2048);
		//定義一個數組,存放http協議請求部分數據
		byte[] buffer = new byte[2048];
		int i=-1;
		i = is.read(buffer);
		for(int j=0;j<i;j++) {
			content.append((char)buffer[j]);
		}
		System.out.println(content);
		parseUrl(content.toString());
	}
	private static void parseUrl(String content) {
		//定義兩個變量存放請求行的兩個控制的位置
		int index1,index2;
		//獲取http請求部分第1個空格的位置
		index1 = content.indexOf(" ");
		if(index1!=-1) {
			index2=content.indexOf(" ",index1+1);
			//獲取http請求部分第二個空格的位置
			if(index2>index1) {
				//獲取字符串穿獲取到本次請求資源的名稱
				url = content.substring(index1+2,index2);
			}
		}
	}
	private static void sendDynamicResource(InputStream is,OutputStream os) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
		//將http協議的響應行和響應頭髮送到客戶端
		os.write("HTTP/1.1 200 OK\n".getBytes());
		os.write("Server:apache-Coyote/1.1\n".getBytes());
		//		os.write("Server:Apache\n".getBytes());
		os.write("Content-type:text/html;charset=utf-8\n".getBytes());
		os.write("\n".getBytes());
		//判斷map中的是否存在key是和本次請求的資源路徑一直
		if(map.containsKey(url)) {
			//如果包含指定的key
			String value = map.get(url);
			//通過反射將對應的java程序加載
			Class clazz = Class.forName(value);
			Servlet servlet = (Servlet)clazz.newInstance();
			//執行init方法
			servlet.init();
			//執行service方法
			servlet.Service(is,os);
		}
	}
	//發送靜態資源
	private static void sendStaticResource(OutputStream os) throws IOException {
		//定義一個字節數組,用於存放本次請求的靜態資源demo01.html的內容
		byte[] bytes = new byte[2048];
		//定義一個文件輸入流,用戶獲取靜態資源demo01.html的內容
		FileInputStream is = null;
		try {
			//創建文件對象File,代表本次要請求的資源demo01.html
			File file = new File(WEB_ROOT,url);
			if(file.exists()) {
				//向客戶端輸出http協議的響應頭和響應行
				os.write("HTTP/1.1 200 OK\n".getBytes());
				os.write("Server:apache-Coyote/1.1\n".getBytes());
				os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
				os.write("\n".getBytes());
				is = new FileInputStream(file);
				int ch = is.read(bytes);
				while(ch!=-1) {
					os.write(bytes,0,ch);
					ch = is.read(bytes);
				}
			}else {
				os.write("HTTP/1.1 404 not found\n".getBytes());
				os.write("Server:apache-Coyote/1.1\n".getBytes());
				os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
				os.write("\n".getBytes());
				String errorMessage = "404 not found";
				os.write(errorMessage.getBytes());
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			if(null!=is) {
				is.close();
				is = null;
			}
		}
	}
}

這裏的server類相比上篇文章的沒有增加多少功能,可以對比一下也就是發送動態的功能吧;接下來是一個servlet接口:

package cn.wcy.mytomcat2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
//所有服務端的java小程序要實現的內容
public interface Servlet {
	//初始化
	public void init();
	//裏邊可以動態寫入內容
	public void Service(InputStream is,OutputStream os) throws IOException;
	//銷燬方法
	public void destroy();
}

然後有兩個實現類:實現類的方法就是重寫了接口的方法而已主要是在server()方法中做的動作;

package cn.wcy.mytomcat2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import cn.wcy.mytomcat2.Servlet;

public class FirstServlet implements Servlet{

	@Override
	public void init() {
		// TODO Auto-generated method stub
		System.out.println("FirstServlet....init");
	}

	@Override
	public void Service(InputStream is, OutputStream os) throws IOException {
		// TODO Auto-generated method stub
		os.write("I am form FirstServlet".getBytes());
		os.flush();
	}

	@Override
	public void destroy() {
		// jdk的servlet接口也是有這個方法的,並且實現類註釋已經寫明瞭說什麼也不做
	}
}

secondServlet所做的事情跟first類似不再贅述;然後是conf.properties

aa=cn.wcy.mytomcat2.FirstServlet
bb=cn.wcy.mytomcat2.SecondServlet

就是用來提供全限定類名的;
整個邏輯也就是通過ServerSocket拿到訪問的客戶端的Socket,然後在拿到OutputStream流之後手動寫入響應頭和響應體,剩下的就交給瀏覽器自己解析就可以了當然我們也可以用web.xml來配置映射關係,但是需要用到xml解析技術我們這裏重點不在這不贅述了;
接下來我們來給apache-tomcat做一下對比用我們這次所定義的類:

  • servlet是java中定義的一個接口,我們寫jsp的controller實現的是HttpServlet類,這個類裏邊有doget和dopost方法,裏邊也包含了對inputstream的解析,不過是以request的形式,重寫的那兩個方法是protect類型的;
  • HttpServlet類是繼承了靜態類GenericServlet,這個類實現了Servlet和ServletConfig兩個接口當然還有這個java.io.Serializable,不過這個接口裏邊方法也是空方法,也就是起了一個標誌性的作用(標誌可序列化)

Java API中java.io.Serializable接口源碼: public interface Serializable {
}

  • 類通過實現java.io.Serializable接口可以啓用其序列化功能。未實現次接口的類無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用於標識可序列化的語義。
  • ServletConfig中的方法:
    1. getInitParameter(): 獲取指定名稱的初始化參數值。
    2. getInitParameterNames():獲取當前 Servlet 所有的初始化參數名稱。其返回值爲枚舉類型 Enumeration。
    3. getServletName():獲取當前 Servlet 的中指定的 Servlet名稱。
    4. getServletContext():獲取到當前 Servlet 的上下文對象 ServletContext。

– ServletConfig接口的特點
每一個servlet都對應一個ServletConfig用於封裝各自的配置信息,即有幾個servlet就會產生幾個ServletConfig對象。

上一篇文章:實現tomcat的靜態資源功能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章