![在这里插入图片描述](https://img-blog.csdnimg.cn/61becb48cd7e4be590b4f3b448f7cb49.jpeg#pic_center)
前言
本文实现一个简易Tomcat,遵循【Tomcat】第八篇:150代码手写Tomcat…
实现
http.TomcatRequest
TomcatRequest.java
package com.sample.http;
import java.io.IOException;
import java.io.InputStream;
public class TomcatRequest {
/**
* 请求方法 get post delete put
*/
private String method;
private String url;
public TomcatRequest(InputStream in) {
try {
// 1.content用来保存InputStream中的http请求信息
String content = "";
byte[] buff = new byte[1024];
int len = 0;
if ((len = in.read(buff)) > 0) {
content = new String(buff, 0, len);
}
// 2.对http请求信息进行处理,得到Method与Url
String line = content.split("\\n")[0];
String[] arr = line.split("\\s");
this.method = arr[0];
this.url = arr[1].split("\\?")[0];
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 返回url
* @return String
*/
public String getUrl() {
return this.url;
}
/**
* 返回请求方法
* @return String
*/
public String getMethod() {
return this.method;
}
}
http.TomcatResponse
TomcatResponse.java
package com.sample.http;
import java.io.IOException;
import java.io.OutputStream;
public class TomcatResponse {
private OutputStream out;
public TomcatResponse(OutputStream out) {
this.out = out;
}
public void write(String s) throws IOException {
StringBuilder sb = new StringBuilder();
// 因为写出的内容要被http协议解析,所以要符合http协议规范,有其要求的响应头(主要是状态码和响应格式)
sb.append("HTTP/1.1 200 OK\n")
.append("Content-Type: text/html;\n")
.append("\r\n")
.append(s);
// IO流写出
this.out.write(sb.toString().getBytes());
}
}
http.TomcatServlet
TomcatServlet.java
package com.sample.http;
import java.io.IOException;
public abstract class TomcatServlet {
// 这里的request与response都是Tomcat对象创建好然后传进来的
public void service(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
if ("GET".equalsIgnoreCase(tomcatRequest.getMethod())) {
doGet(tomcatRequest, tomcatResponse);
}
else {
doPost(tomcatRequest, tomcatResponse);
}
}
// 这里是模板方法模式,交给子类去具体实现
protected abstract void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException;
protected abstract void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException;
}
SimpleTomcat
SimpleTomcat.java
package com.sample;
import com.sample.http.TomcatRequest;
import com.sample.http.TomcatResponse;
import com.sample.http.TomcatServlet;
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.Map;
import java.util.Properties;
/**
* Tomcat核心类
*/
public class SimpleTomcat {
private int port = 8080;
private ServerSocket serverSocket;
private Map<String, TomcatServlet> servletMapping = new HashMap<>();
private Properties webxml = new Properties();
private void init() {
try {
// 1.加载web.properties文件
String WEB_INF = this.getClass().getResource("/").getPath();
FileInputStream fileInputStream = new FileInputStream(WEB_INF + "web.properties");
webxml.load(fileInputStream);
// 2.遍历配置文件,寻找url与servlet映射关系配置
for (Object o : webxml.keySet()) {
String key = o.toString();
// 以url结尾的key就是要映射的路径,下面是两条配置示例:
// servlet.one.url=/firstServlet.do
// servlet.one.className=com.yzh.tomcat.servlet.FirstServlet
if (key.endsWith(".url")) {
// 去掉.url就是servlet的name(servlet.one)
String servletName = key.replaceAll("\\.url$", "");
// 2.1 获取到url(/first.do)
String url = webxml.getProperty(key);
// 2.2 获取对应servlet全类名(com.yzh.tomcat...FirstServlet),并通过反进行实例化
String className = webxml.getProperty(servletName + ".className");
// 注:这里是将所有Servlet都强转为TomcatServlet,所以一定要继承TomcatServlet
TomcatServlet tomcatServlet = (TomcatServlet) Class.forName(className).newInstance();
// 3.将url与servlet实例保存到servletMapping中(单例模式)
servletMapping.put(url, tomcatServlet);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 启动tomcat
*/
public void start() {
// 1.调用init,目的是得到servletMapping的映射关系
init();
try {
// 2.通过BIO创建socket的服务端,在指定端口开始监听
serverSocket = new ServerSocket(this.port);
System.out.println("SimpleTomcat已启动,监听的端口是" + this.port);
// 3.用一个死循环持续等待并处理用户请求
while (true) {
Socket client = serverSocket.accept();
// process是具体处理请求的逻辑,参数是当前连接的Socket
process(client);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 具体处理请求
* 1.创建IO流,并包装成Request与Response
* 2.获取请求Url,取出对应Servlet进行处理
*/
private void process(Socket client) throws IOException {
// 1.获取IO流,并封装成Request与Response
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
// 注:这里要明白,每次请求的Request和Response都是不同的(因为连接时的socket不同),他俩的作用域仅为当前会话
TomcatRequest request = new TomcatRequest(in);
TomcatResponse response = new TomcatResponse(out);
// 获取请求URL,寻找相应Servlet进行处理
String url = request.getUrl();
// 2.判断改url是否有对应的Servlet实例
if (servletMapping.containsKey(url)) {
// 如果有,调用service方法进行处理
servletMapping.get(url).service(request, response);
}
else {
// 如果没有,写出404
response.write("404 - Not Found");
}
// 3.关闭本次连接相关资源
out.flush();
out.close();
in.close();
client.close();
}
public static void main(String[] args) {
new SimpleTomcat().start();
}
}
servlet.FirstServlet
package com.sample.servlet;
import com.sample.http.TomcatRequest;
import com.sample.http.TomcatResponse;
import com.sample.http.TomcatServlet;
import java.io.IOException;
public class FirstServlet extends TomcatServlet {
@Override
protected void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
tomcatResponse.write("this is FirstServlet!");
}
@Override
protected void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
doPost(tomcatRequest, tomcatResponse);
}
}
servlet.SecondServlet
SecondServlet.java
package com.sample.servlet;
import com.sample.http.TomcatRequest;
import com.sample.http.TomcatResponse;
import com.sample.http.TomcatServlet;
import java.io.IOException;
public class SecondServlet extends TomcatServlet {
@Override
protected void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
tomcatResponse.write("Hello world!");
}
@Override
protected void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
doPost(tomcatRequest, tomcatResponse);
}
}
配置
web.properties
servlet.one.url=/firstServlet
servlet.one.className=com.sample.servlet.FirstServlet
servlet.two.url=/secondServlet
servlet.two.className=com.sample.servlet.SecondServlet
文件结构
![在这里插入图片描述](https://img-blog.csdnimg.cn/a88d2d6140dd4d47af7a2bf70912fb29.png)
运行
运行我们的 SimpleTomcat.java
![在这里插入图片描述](https://img-blog.csdnimg.cn/ee5169a9e360458b95e01d6404a6cb9d.png)
在浏览器中输入
http://localhost:8080/firstServlet
或者
http://localhost:8080/secondServlet
可以看到
![在这里插入图片描述](https://img-blog.csdnimg.cn/c00a1e9b5a5048e0bce73462f4562f64.png)
参考
https://blog.csdn.net/weixin_43935927/article/details/108743213