从零手动实现简易Tomcat

2,475 阅读4分钟
Hello大家好,本章我们自己实现一个简易版的tomcat 。有问题可以联系我mr_beany@163.com。另求各路大神指点,感谢

程序的运行少不了服务器的支持,而tomcat因其性能稳定,免费等优点,深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,为目前比较流行的 Web 应用服务器。

那么到底它是怎么运行的呢?今天我们来实现一个简化版tomcat来感受一下。

一:导入解析xml的jar包

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>

二:编写XML解析类

package com.example.demo.utils;

import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * @author zy
 */
public class XmlUtils {
    /**
     * 定义解析器和文档对象
     */
    public SAXReader saxReader;
    public Document document;

    public XmlUtils(String path) {
        //获取解析器
        saxReader = new SAXReader();
        try {
            //获取文档对象
            document = saxReader.read(path);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取节点下的所有节点
     *
     * @param name
     * @return
     */
    public List<Element> getNodes(String name) {
        Element root = document.getRootElement();
        return root.elements(name);
    }
}

三:实现Request和Response

其中Request是对浏览器的请求的封装,而Response是对浏览器请求的响应,换而言之就是Request 用来取出请求信息,而 Response 则用来添加要返回给浏览器的信息。

创建Request.java

package com.example.demo.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author zy
 */
public class Request {

    private String method;

    private String url;

    public Request(InputStream inputStream) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String[] methodAndUrl = bufferedReader.readLine().split(" ");
        this.method = methodAndUrl[0];
        this.url = methodAndUrl[1];
        System.out.println("请求类型:"+ method);
        System.out.println("请求路径:"+ url);
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
    
}

创建Response.java

package com.example.demo.http;

import java.io.OutputStream;

/**
 * @author zy
 */
public class Response {

    private OutputStream outputStream;

    private String write;

    public Response(OutputStream outputStream){
        this.outputStream = outputStream;
    }

    public String getWrite() {
        return write;
    }

    public void setWrite(String write) {
        this.write = write;
    }

}

四:实现Servlet

先做个接口

package com.example.demo.servlet;

import com.example.demo.http.Request;
import com.example.demo.http.Response;

/**
 * @author zy
 */
public abstract class AbstractServlet {

    public abstract void doGet(Request request, Response response);

    public abstract void doPost(Request request, Response response);
}

再来实现自己的Servlet

package com.example.demo.servlet;

import com.example.demo.http.Request;
import com.example.demo.http.Response;

/**
 * @author zy
 */
public class FirstServlet extends AbstractServlet {

    @Override
    public void doGet(Request request, Response response) {
        response.setWrite("我的第一个Servlet");
    }

    @Override
    public void doPost(Request request, Response response) {
        this.doGet(request, response);
    }
}

五:创建web.xml

这里配置了请求路径的servlet的对应关系,文件放在resources文件夹下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

    <servlet>
        <servlet-name>first.html</servlet-name>
        <servlet-class>com.example.demo.servlet.FirstServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>first.html</servlet-name>
        <url-pattern>/first.html</url-pattern>
    </servlet-mapping>

</web-app>

六,创建tomcat,初始化配置文件

package com.example.demo.tomcat;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;

import com.example.demo.utils.XmlUtils;
import org.dom4j.Element;

/**
 * @author zy
 */
public class Tomcat {
    /**
     * 设置端口号
     */
    private static final int PORT = 8080;

    public static final HashMap<String, Object> SERVLET_MAPPING = new HashMap<>();

    public static final HashMap<String, Object> SERVLET = new HashMap<>();

    /**
     * 控制服务器启动关闭
     */
    public boolean tomcatStarBool = true;

    private void init() {
        InputStream io = null;
        try {
            System.out.println("加载配置文件开始");
            //读取配置文件
            XmlUtils xml = new XmlUtils(XmlUtils.class.getResource("/")+"web.xml");
            //将所有的类都存储到容器中
            List<Element> list = xml.getNodes("servlet");
            for (Element element : list) {
                SERVLET.put(element.element("servlet-name").getText(),
                        Class.forName(element.element("servlet-class").getText()).newInstance());
            }
            //映射关系创建
            List<Element> list2 = xml.getNodes("servlet-mapping");
            for (Element element : list2) {
                SERVLET_MAPPING.put(element.element("url-pattern").getText(),
                        element.element("servlet-name").getText());
            }
            System.out.println("加载配置文件结束");
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (io != null) {
                try {
                    io.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

七:创建处理请求任务的SocketProcess

package com.example.demo.tomcat;

import com.example.demo.http.Request;
import com.example.demo.http.Response;
import com.example.demo.servlet.AbstractServlet;

import java.io.OutputStream;
import java.net.Socket;

/**
 * @author zy
 */
public class SocketProcess extends Thread{

    protected Socket socket;

    public SocketProcess(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            Request request = new Request(socket.getInputStream());
            Response response = new Response(socket.getOutputStream());
            String servletName = (String) Tomcat.SERVLET_MAPPING.get(request.getUrl());
            if(servletName!=null && !servletName.isEmpty()) {
                //映射有的话找到对应的对象
                AbstractServlet servlet = (AbstractServlet) Tomcat.SERVLET.get(servletName);
                if(servlet!=null) {
                    servlet.doGet(request, response);
                }else {
                    System.out.println("找不到对应的servlet");
                }
            }else {
                System.out.println("找不到对应的servletMapping");
            }
            String res = response.getWrite();
            OutputStream outputStream = socket.getOutputStream();

            outputStream.write(res.getBytes("GBK"));
            outputStream.flush();
            outputStream.close();

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

八:在tomcat.java中添加启动方法

private void start() {
    try {
        ServerSocket serverSocket = new ServerSocket(PORT);
        System.out.println("Tomcat 服务已启动,地址:localhost ,端口:" + PORT);
        this.init();
        //持续监听
        do {
            Socket socket = serverSocket.accept();
            //处理任务
            Thread thread = new SocketProcess(socket);
            thread.start();
        } while (tomcatStarBool);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Tomcat tomcat = new Tomcat();
    tomcat.start();
}

九:测试

启动在tomcat.java中运行void main方法


出现如下字样代表启动成功,然后地址栏中输入

http://localhost:8080/first.html

?????为什么是这样。。。。。。

啊!原来没有添加响应头信息

Response.java中加入

/**
 * 响应头信息
 */
public static final String RESPONSE_HEADER ="HTTP/1.1 200 \r\n"
        + "Content-Type: text/html\r\n"
        + "\r\n";

SocketProcess.java中

String res = response.getWrite();

改成

String res = Response.RESPONSE_HEADER + response.getWrite();

重新启动,访问




结尾

祝大家新年快乐。感谢支持!


其他文章:

从零搭建自己的springboot后台框架

码云地址: gitee.com/beany/mySpr…

GitHub地址: github.com/MyBeany/myS…