手写Tomcat
手写Tomcat
- Tomcat详解
- 划分结构
- 详解结构
- 代码示例
- req
- HttpServletRequest
- HttpServletResponse
- Servlet 接口
- GenericServlet 抽象类
- HttpServlet 抽象类
- Util 工具包
- ResponseUtil
- SearchClassUtil
- Webservlet 注解
- webapps.myweb
- LoginServlet
- ShowServlet
- ServletConfigMapping
- MyTomcat
手写一个简单的 Tomcat 部分,涉及到 Servlet 容器的实现,虽然无法做到完整地模拟 Tomcat 的复杂性,但可以通过简化的代码示例来展示 Tomcat 作为 Servlet 容器的核心概念和工作原理。
Tomcat 的关键任务是接收 HTTP 请求、路由到相应的 Servlet 进行处理,并将响应返回给客户端。
Tomcat详解
划分结构
首先我们进行划分结构,如下图:
详解结构
- servlet – HttpServlet – GenericServlet 表示了Servlet的继承实现关系,标志着Servlet的声明周期
- req 中的HttpServletRequest 和 HttpServletResponse 代表着 request 和 response 请求
- Util 是工具包
- webapps.myweb 表示的是自己的web项目
- 最后是Tomcat 和 Servlet容器的构建
代码示例
req
HttpServletRequest
public class HttpServletRequest {
private String method;
private String path;
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
HttpServletResponse
import java.io.IOException;
import java.io.OutputStream;
public class HttpServletResponse {
private OutputStream outputStream;
public HttpServletResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void writeServlet(String context) throws IOException {
outputStream.write(context.getBytes());
}
}
Servlet 接口
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;
/**
* 定义了servlet的生命周期
*/
public interface Servlet {
void init(); // 初始化servlet
void service(HttpServletRequest request, HttpServletResponse response) throws Exception; // 服务
void destroy(); // 销毁
}
GenericServlet 抽象类
public abstract class GenericServlet implements Servlet{
public void init() {
System.out.println("初始化servlet------");
}
public void destroy() {
System.out.println("销毁servlet------");
}
}
HttpServlet 抽象类
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;
public abstract class HttpServlet extends GenericServlet{
public void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
if(request.getMethod().equals("GET")){
doGet(request,response);
}else if(request.getMethod().equals("POST")){
doPost(request,response);
}
}
public abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception;
public abstract void doPost(HttpServletRequest request, HttpServletResponse response)throws Exception;
}
Util 工具包
ResponseUtil
public class ResponseUtil {
public static final String responseHeader200 = "HTTP/1.1 200 \r\n"+
"Content-Type:text/html; charset=utf-8 \r\n"+"\r\n";
public static String getResponseHeader404(){
return "HTTP/1.1 404 \r\n"+
"Content-Type:text/html; charset=utf-8 \r\n"+"\r\n" + "404";
}
public static String getResponseHeader200(String context){
return "HTTP/1.1 200 \r\n"+
"Content-Type:text/html; charset=utf-8 \r\n"+"\r\n" + context;
}
}
SearchClassUtil
/**'
* 扫描com.qcby.webapps目录下面的文件,获取每一个Java文件的全类名
*/
public class SearchClassUtil {
public static List<String> classPaths = new ArrayList<String>();
public static List<String> searchClass(){
//需要扫描的包名
String basePack = "com.qcby.webapps";
//将获取到的包名转换为路径
String classPath = SearchClassUtil.class.getResource("/").getPath();
basePack = basePack.replace(".", File.separator);
String searchPath = classPath + basePack;
doPath(new File(searchPath),classPath);
//这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
return classPaths;
}
/**
* 该方法会得到所有的类,将类的绝对路径写入到classPaths中
* @param file
*/
private static void doPath(File file,String classpath) {
if (file.isDirectory()) {//文件夹
//文件夹我们就递归
File[] files = file.listFiles();
for (File f1 : files) {
doPath(f1,classpath);
}
} else {//标准文件
//标准文件我们就判断是否是class文件
if (file.getName().endsWith(".class")) {
String path = file.getPath().replace(classpath.replace("/","\\").
replaceFirst("\\\\",""),"").replace("\\",".").
replace(".class","");
//如果是class文件我们就放入我们的集合中。
classPaths.add(path);
}
}
}
public static void main(String[] args) {
List<String> classes = SearchClassUtil.searchClass();
for (String s: classes) {
System.out.println(s);
}
}
}
Webservlet 注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 表明注解的生命周期,SOURCE:源代码,CLASS:编译成class文件,RUNTIME:运行时 源文件时就有效
@Target(ElementType.TYPE) // 表明@Webservlet注解只能用于类上
public @interface Webservlet {
// String path = null;
String urlMapping() default ""; // 自定义的servlet路径
}
webapps.myweb
随便给出两个web项目
LoginServlet
import com.qcby.Util.ResponseUtil;
import com.qcby.Util.Webservlet;
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;
import com.qcby.servlet.HttpServlet;
import java.io.IOException;
@Webservlet(urlMapping = "/Login")
public class LoginServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("我是Login");
response.writeServlet(ResponseUtil.getResponseHeader200("hello 你好"));
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) {
}
}
ShowServlet
import com.qcby.Util.Webservlet;
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;
import com.qcby.servlet.HttpServlet;
@Webservlet(urlMapping = "/show")
public class ShowServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) {
}
}
ServletConfigMapping
import com.qcby.Util.SearchClassUtil;
import com.qcby.Util.Webservlet;
import com.qcby.servlet.HttpServlet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* servlet 容器
*/
public class ServletConfigMapping {
// 扫描com.qcby.webapps包下的所有类,并获取它的全类名
// 根据全路径名获取类对象,获取@Webservlet注解,获取urlMapping属性值
// 创建servlet容器,将path和对象写入容器当中
public static Map<String, HttpServlet> servletMap = new HashMap<>();
// 将path和对象写入容器中
public static void init() throws Exception {
List<String> classPath = SearchClassUtil.searchClass();
for(String path: classPath){
getMessage(path);
}
}
// 利用反射获取类的注释信息
public static void getMessage(String classPath) throws Exception {
Class clazz = Class.forName(classPath);
// 注释对象
Webservlet webservlet = (Webservlet) clazz.getDeclaredAnnotation(Webservlet.class);
HttpServlet servlet = (HttpServlet) clazz.getDeclaredConstructor().newInstance();
servletMap.put(webservlet.urlMapping(),servlet);
}
}
MyTomcat
import com.qcby.servlet.HttpServlet;
import com.qcby.servlet.req.HttpServletRequest;
import com.qcby.servlet.req.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTomcat {
static HttpServletRequest request = new HttpServletRequest();
public static void main(String[] args) throws Exception {
ServletConfigMapping.init();
//1.创建serversocket对象,持续监听8080端口
ServerSocket serverSocket = new ServerSocket(8080);
while (true){
//accept():阻塞监听 ,当代码执行到这一样,如果没有数据到来,那么循环阻塞在这里。如果有数据到来,就继续向下执行
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
HttpServletResponse response = new HttpServletResponse(outputStream);
int count = 0;
while (count == 0){
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);
String Context = new String(bytes);
System.out.println(Context);
//解析数据
if(Context.equals("")){
System.out.println("你输入了一个空请求");
}else {
String firstLine = Context.split("\\n")[0]; //根据换行来获取第一行数据
request.setPath(firstLine.split("\\s")[1]);
request.setMethod(firstLine.split("\\s")[0]);
}
// 根据访问而定路径为key值从map集合当中尝试获取数据
if(ServletConfigMapping.servletMap.containsKey(request.getPath())){
HttpServlet servlet = ServletConfigMapping.servletMap.get(request.getPath());
servlet.service(request,response);
}
}
}
}