手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址)

文章参考博客 https://www.jianshu.com/p/dce1ee01fb90.

源码链接: https://github.com/LingYunYe/MyTomcat.

一、概述

Tomcat是非常流行的Web Server,它还是一个满足Servlet规范的容器。那么想一想,Tomcat和我们的Web应用是什么关系?
  
  从感性上来说,我们一般需要把Web应用打成WAR包部署到Tomcat中,在我们的Web应用中,我们

要指明URL被哪个类的哪个方法所处理(不论是原始的Servlet开发,还是现在流行的Spring MVC都必须

指明)。
  
   由于我们的Web应用是运行在Tomcat中,那么显然,请求必定是先到达Tomcat的。Tomcat对于

请求实际上会进行下面的处理:

第一:提供Socket服务
  Tomcat的启动,必然是Socket服务,只不过它支持HTTP协议而已!
  
  这里其实可以扩展思考下,Tomcat既然是基于Socket,那么是基于BIO or NIO or AIO呢?

第二:进行请求的分发
  要知道一个Tomcat可以为多个Web应用提供服务,那么很显然,Tomcat可以把URL下发到不同的

Web应用。

第三:需要把请求和响应封装成request/response
  我们在Web应用这一层,可从来没有封装过request/response的,我们都是直接使用的,这就是因

为Tomcat已经为你做好了!

目录图如下
手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址)_第1张图片

二、请求封装

1 、 MyRequest

package com.ye.myTomcat;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class MyRequest {
	private String url;
	private String method;
	private Map<String, String> allParameter = new HashMap<String, String>();

	public String getParameter(String paramName) {
		return allParameter.get(paramName);
	}

	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;
	}

	@Override
	public String toString() {
		return "MyRequest [url=" + url + ", method=" + 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);
		}
		System.out.println(httpRequest);
		String httpHead = httpRequest.split("\n")[0];
		//get请求带参数
		if (httpHead.indexOf("?") > 0) {
			String url1 = httpHead.split("\\s")[1];
			url = url1.substring(0, url1.indexOf("?"));
			String[] param = url1.substring(url1.indexOf("?") + 1).split("&");
			for (int i = 0; i < param.length; i++) {
				String key = param[i].substring(0, param[i].indexOf("="));
				String value = param[i].substring(param[i].indexOf("=") + 1);
				//将参数以键值对形式放入map集合
				allParameter.put(key, value);
			}
		//get请求不带参数
		} else {
			url = httpHead.split("\\s")[1];
		}

		method = httpHead.split("\\s")[0];
		System.out.println("MyRequest---------------------->" + this);
	}
}

MyRequest类可以通过输入流,对HTTP协议进行解析,拿到了HTTP请求头的方法,URL以及url参数,http协议通过输入流传过来长如下样子。所以可以根据如上字符串操作获取需要的数据。
手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址)_第2张图片
2 、 MyResponse

package com.ye.myTomcat;

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

public class MyResponse {
	private OutputStream outputStream;
	public MyResponse(OutputStream outputStream) {
		this.outputStream = outputStream;
	}
	public void write(String content) throws IOException {
		//http 响应协议
		StringBuffer httpResponse = new StringBuffer();
		httpResponse.append("HTTP/1.1 200 OK\n")
		.append("Content-Type: text/html;charset=UTF-8\n")
		.append("\r\n")
		.append("")
		.append(content)
		.append("");
		outputStream.write(httpResponse.toString().getBytes());
		outputStream.close();
	}
}

MyResponse 基于http协议的格式进行输出写入

三、 servlet的创建与配置

前文说Tomcat是满足Servlet规范的容器,那么自然Tomcat需要提供API。这里你看到了Servlet常见的

doGet/doPost/service方法。
1、 父类 MyServlet

package com.ye.myTomcat;

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);
		}
	};

}

可以看到里面定义了常用的 get 和 post 方法
2 、 子类FindServlet,HelloWorldServlet(FaviconServlet暂时不贴代码,后面将),两个具体的实现类
①FindServlet

package com.ye.myTomcat;

import java.io.IOException;

public class FindServlet extends MyServlet{

	@Override
	public void doGet(MyRequest myRequest, MyResponse myResponse) {
		// TODO Auto-generated method stub
		try {
			myResponse.write("GET FindServlet");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void doPost(MyRequest myRequest, MyResponse myResponse) {
		// TODO Auto-generated method stub
		try {
			myResponse.write("POST FindServlet");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

② 、 HelloWorldServlet

package com.ye.myTomcat;

import java.io.IOException;

public class HelloWorldServlet extends MyServlet {

	@Override
	public void doGet(MyRequest myRequest, MyResponse myResponse) {
		String name = myRequest.getParameter("name");
		String password  = myRequest.getParameter("password");
		StringBuffer content = new StringBuffer();
		content.append("method         GET  
"
) .append("className      HelloWorldServlet
"
) .append("name      "+ name +"
"
) .append("password      "+ password); // TODO Auto-generated method stub try { myResponse.write(content.toString()); } catch (IOException e) { e.printStackTrace(); } } @Override public void doPost(MyRequest myRequest, MyResponse myResponse) { // TODO Auto-generated method stub try { myResponse.write("POST HelloWorldServlet"); } catch (IOException e) { e.printStackTrace(); } } }

3 、 配置servlet
类似与传统 web.xml 中和来进行指定哪个URL交给哪个servlet进行处理
①、 ServletMapping (servletName   servlet名字,url  url路径,clazz  类路径)

package com.ye.myTomcat;

public class ServletMapping {
	private String servletName;
	private String url;
	private String clazz;
	
	public ServletMapping() {
		super();
	}
	public ServletMapping(String servletName, String url, String clazz) {
		super();
		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;
	}
}

② 、 ServletMappingConfig

package com.ye.myTomcat;

import java.util.ArrayList;
import java.util.List;

public class ServletMappingConfig {
	public static List<ServletMapping> servletMappingList = new ArrayList<ServletMapping>();
	//相当web.xml中通过来进行指定哪个URL交给哪个servlet进行处理。
	static {
		servletMappingList.add(new ServletMapping("findServlet", "/find", "com.ye.myTomcat.FindServlet"));
		servletMappingList.add(new ServletMapping("helloWorldServlet", "/hello", "com.ye.myTomcat.HelloWorldServlet"));
		//servletMappingList.add(new ServletMapping("faviconServlet", "/favicon.ico", "com.ye.myTomcat.FaviconServlet"));
	}
}

4 、 tomcat 入口类 MyTomcat

package com.ye.myTomcat;

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;

public class MyTomcat {
	private int port = 8080;
	private Map<String, String> urlServletMap = new HashMap<String, String>();

	public MyTomcat(int port) {
		this.port = port;
	}

	public void start() {
		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();
				//完成构造方法后,就可以获取 http协议的方法名,url,以及参数
				MyRequest myRequest = new MyRequest(inputStream);
				MyResponse myResponse = new MyResponse(outputStream);
				dispatch(myRequest,myResponse);
				socket.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(serverSocket !=null) {
				try {
					serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void initServletMapping() {
	//初始化 ServletMapping
		for (ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
			urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz());
		}
	}

	private void dispatch(MyRequest myRequest, MyResponse myResponse) {
		String clazz = urlServletMap.get(myRequest.getUrl());
		try {
		//通过初始化的ServletMapping获取servlet的类路径,从而通过反射实例化
			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(8080).start();
	}
}

5 、 测试
以我的代码为例,访问http://localhost:8080/hello?name=zhangsan&password=123456
你可能会看到如下结果
手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址)_第3张图片
/hello成功,你可能会疑惑我又访问/favicon.ioc,他怎么自己访问了,其实/favicon.ioc代表加载图标
如这些东西,他会默认加载你没有就会fail
在这里插入图片描述
后台你可能也会报错,ServerSocket会关闭,是空指针,因为你没有配置这个接口当然会是这样
手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址)_第4张图片
当然你现在知道是什么原因造成肯定也有解决办法,你可以做if判断,如果为/favicon.ioc不做任何处理,也可以在ServletMappingConfig中配置/favicon.ioc,我的是做了配置,新建了一个类

//ServletMappingConfig配置
servletMappingList.add(new ServletMapping("faviconServlet", "/favicon.ico", "com.ye.myTomcat.FaviconServlet"));

修改过后现在访问就不会报错,如图,现在已经用get请求实现简单的前后台交互
手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址)_第5张图片
post请求如何处理,后续会添加进来,源码里又加了MySQL数据库连接,数据库接口以及登录验证,需要的博友自行去git拉取。

四、感悟(tiangouyulu)

昨天你领完红包就把我删了,我陷入久久地沉思。我想这其中一定有什么含义,原来你是在欲擒故

纵,嫌我不够爱你。无理取闹的你变得更加可爱了,我会坚守我对你的爱的。你放心好啦!今天发工资

了,发了1850,给你微信转了520,支付宝1314,还剩下16。给你发了很多消息你没回。剩下16块我在

小卖部买了你爱吃的老坛酸菜牛肉面,给你寄过去了。希望你保护好食欲,我去上班了爱你~~

你可能感兴趣的:(手写一个简单的 tomcat,并于get请求进行登录验证(附源码地址))