Hello 大家好, 本章我们自己实现一个简易版的 tomcat . 有问题可以联系我 mr_beany@163.com. 另求各路大神指点, 感谢
程序的运行少不了服务器的支持, 而 tomcat 因其性能稳定, 免费等优点, 深受 Java 爱好者的喜爱并得到了部分软件开发商的认可, 为目前比较流行的 web 应用服务器.
那么到底它是怎么运行的呢? 今天我们来实现一个简化版 tomcat 来感受一下.
一: 导入解析 xml 的 jar 包
- dom4j
- dom4j
- 1.6.1
- >
二: 编写 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"?>
- 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">
first.html
com.example.demo.servlet.FirstServlet
first.HTML
/first.HTML
>
六, 创建 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 后台框架
码云地址: https://gitee.com/beany/mySpringBoot
GitHub 地址: https://github.com/MyBeany/mySpringBoot
来源: https://juejin.im/entry/5c516476f265da61736aab3e