原生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的静态资源功能

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