Tomcat 到底干了啥

2022-09-14 13:28:14 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

道阻且长,行则将至。请相信我,你一定会更优秀!

此文为Tomcat系列的第一篇,Tomcat的整体架构个人感觉非常有意思,本文我们先非常简单的入个门。

先抛开对 Tomcat 的认识,想一下,如果没有 Tomcat,我们想访问到我们的工程需要干些什么?

1、想要在浏览器访问工程,需要 URL,那么就是要有 IP 和端口,(我们这里拿本机说话,所以采用 localhost),换句话说,在本机上,我们需要一个提供服务的端口;

2、这个服务要能够识别我在工程中web.xml 中配置的访问路径,并且对应到我自己的servlet 处理器,然后做我的业务逻辑;

3、我的业务逻辑做完后,要把结果通知给 Client;

OK,就是这么简单,就是想把我的代码和我的客户端 Client互动起来。本篇文章不研究 Tomcat的类加载,B/N/AIO及源码,简单化和大家聊,说白了:

我就是想让我的代码跑起来,不用 Tomcat,到底行不行?

Tomcat 本身是一个 servlet 容器,我们现在不依赖它的servlet 管理,也就是我们自己写 HttpServletRequest,HttpServletResponse,HttpServlet 所有的处理都是依赖我们自己写的 servlet。自定义我们自己的 MyServletRequest相当于 HttpServletRequest,我们自己的MyServletResponse 相当于HttpServletResponse


目录

第一步:创建端口,开启服务。

第二步:我们的 Tomcat内核处理器:

第三步:如果处理器匹配 URL找到了我们工程业务的 servlet,如:

第一步:创建端口,开启服务。

代码语言:javascript复制
package com.tomcat.start;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.tomcat.process.MyProcess;
import com.tomcat.servlet.MyServlet;
/**
 * function: My Tomcat Starter
 * @author zhanghaolin
 * @date 2018年7月25日
 */
public class MyStarter {

	// 自定义端口
	private static final Integer PORT = 8090;
	// 加载工程的URL-SERVLET映射
	public static Map<String, Object> servletMapping = new HashMap<String, Object>();
	
	/**
	 * 
	 * function: ./start.sh
	 * @param args
	 * @author zhanghaolin
	 */
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		try {
			System.out.println("开始启动!");
			System.out.println("初始化中 ...");
			// 创建服务端口
			ServerSocket serverSocket = new ServerSocket(PORT);
			// 初始化(加载web.xml)
			init();
			System.out.println("启动完毕!");
			do {
				// 接收客户端连接
				Socket accept = serverSocket.accept();
				// 开启新线程让容器对连接进行处理
				Thread thread = new MyProcess(accept);
				thread.start();
			} while (Boolean.TRUE);	// 一直处于监听状态
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 
	 * function: 解析web.xml中配置的servlet,这儿解析只是个demo工具方法,别吐槽代码哈哈
	 * @author zhanghaolin
	 */
	private static void init() {
		InputStream resourceAsStream = MyStarter.class.getClassLoader().getResourceAsStream("web.xml");
	    SAXReader saxReader = new SAXReader();
	    try {
			Document document = saxReader.read(resourceAsStream);
			Element rootElement = document.getRootElement();
			List<Element> elements = rootElement.elements();
			for (int i = 0, length=elements.size(); i < length; i  ) {
				Element element = elements.get(i);
				List<Element> es = element.elements();
				for (int j = 0, lgth=es.size(); j < lgth; j  ) {
					Element element2 = es.get(j);
					String ename1 = element2.getName().toString();
					if ("servlet-name".equals(ename1) && "servlet".equals(element.getName().toString())) {
						String servletName = element2.getStringValue();
						Element ele2 = element.element("servlet-class");
						String classname = ele2.getStringValue();
						List<Element> elements2 = rootElement.elements("servlet-mapping");
						for (int k = 0, lk=elements2.size(); k < lk; k  ) {
							Element element4 = elements2.get(k);
							List<Element> es3 = element4.elements();
							for (int op = 0, opp=es3.size(); op < opp; op  ) {
								if ("servlet-name".equals(es3.get(op).getName().toString())
										&& servletName.equals(es3.get(op).getStringValue())) {
									Element element7 = element4.element("url-pattern");
									String urlPattern = element7.getStringValue();
									servletMapping.put(urlPattern, (MyServlet) Class.forName(classname).newInstance());
									System.out.println("==> 加载 "  classname   ":"  urlPattern);
								}
							}
							
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != resourceAsStream) {
				try {
					resourceAsStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
代码语言:javascript复制
<?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_3_0.xsd"
	version="3.0">
	<servlet>
		<servlet-name>servlet1</servlet-name>
		<servlet-class>com.haolin.yewu.WodeServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>servlet1</servlet-name>
		<url-pattern>/test</url-pattern>
	</servlet-mapping>
	
</web-app>

第二步:我们的 Tomcat内核处理器:

代码语言:javascript复制
package com.tomcat.process;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.tomcat.initparam.MyServletRequest;
import com.tomcat.initparam.MyServletResponse;
import com.tomcat.servlet.MyServlet;
import com.tomcat.start.MyStarter;
/**
 * 
 * function: 自定义容器处理器
 * @author zhanghaolin
 * @date 2018年7月25日
 */
public class MyProcess extends Thread {

	private static final String SUCCESS = "200";
	private static final String NOT_FOUND = "404";
	
	private Socket socket;
	private String address;
	private Integer port;
	private String status;
	private String url;
	
	public MyProcess(Socket socket) {
		this.socket = socket;
		InetAddress inetAddress = socket.getInetAddress();
		this.address = inetAddress.getHostAddress();
		this.port = socket.getLocalPort();
	}
	
	@Override
	public void run() {
		try {
			// 接收到请求,处理请求携带信息
			// 自定义 request进行封装
			MyServletRequest request = new MyServletRequest(socket.getInputStream());
			// 自定义 response进行封装
			MyServletResponse response = new MyServletResponse(socket.getOutputStream());
			String url = request.getUrl();
			this.url = url;
			// 通过URL匹配Servlet(这里我们的servlet-mapping使用等于匹配,还可以采取正则匹配,如*.do)
			MyServlet servlet = (MyServlet) MyStarter.servletMapping.get(url);
			if (null != servlet) {
				// 容器中存在处理该请求的Servlet,假设程序没有运行错误,状态200
				this.status = SUCCESS;
				servlet.service(request, response);
			} else {
				// 容器中不存在处理该请求的Servlet,状态404
				this.status = NOT_FOUND;
				OutputStream outputStream = response.getOutputStream();
				outputStream.write(new String(MyServletResponse.RESPONSE_HEADER   "Welcome! error: Cannot find the servlet!").getBytes());
				outputStream.flush();
				outputStream.close();
			}
			if (!"/favicon.ico".equals(url)) {
				// 简单记录我们自己的访问日志
				logRecord();
			} 
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != socket) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 
	 * function: 日志记录
	 * @author zhanghaolin
	 * @date 2018年7月25日   下午4:35:34
	 */
	@SuppressWarnings("resource")
	private void logRecord(){
		try {
			Date date = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
			String dateStr = sdf.format(date);
			String record = dateStr   " "   this.address   ":"   this.port
					  " ==> "   this.url   " , response:"   this.status   "rn";
			File file = new File("d:\mytomcat-log\mylog.log");
			if (!file.exists()) {
				file.createNewFile();
			}
			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true));
			bufferedWriter.write(record);
			bufferedWriter.flush();
			bufferedWriter.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

处理器用户接受客户端请求,并且对请求作出处理。在我们的处理器中用到了我们自定义的 MyServletRequest和 MyServletResponse

如下:

代码语言:javascript复制
package com.tomcat.initparam;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * function: 自定义Request相当于HttpServletRequest
 * @author zhanghaolin
 * @date 2018年7月25日
 */
public class MyServletRequest {

	// 请求方式
	private String method;
	// 请求URL
	private String url;
	// 携带参数
	private String[] paramArray;
	
	public MyServletRequest(InputStream inputStream) {
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
		try {
			String[] split = bufferedReader.readLine().split(" ");
			if (split.length == 3) {
				this.method = split[0];
				String allUrl = split[1];
				if (allUrl.contains("?")) {
					this.url = allUrl.substring(0, allUrl.indexOf("?"));
					String params = allUrl.substring(allUrl.indexOf("?") 1);
					paramArray = params.split("&");
				} else {
					this.url = allUrl;
				}
				if (allUrl.endsWith("ico")) {  
					return;  
				}  
			}
			
			// 注:split[2] 是 协议:HTTP/1.1
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String getMethod() {
		return method;
	}

	public String getUrl() {
		return url;
	}

	public String[] getParamArray() {
		return paramArray;
	}

}
代码语言:javascript复制
package com.tomcat.initparam;

import java.io.OutputStream;

public class MyServletResponse {

	private OutputStream outputStream;
	
	// 添加Response响应头
	public static final String RESPONSE_HEADER=
			"HTTP/1.1 200 rn"
              "Content-Type: text/htmlrn"
              "rn";
	
	public MyServletResponse(OutputStream outputStream) {
		this.outputStream = outputStream;
	}

	public OutputStream getOutputStream() {
		return outputStream;
	}
	
}

第三步:如果处理器匹配 URL找到了我们工程业务的 servlet,如:

自定义 Tomcat servlet超级父类:

代码语言:javascript复制
package com.tomcat.servlet;

import com.tomcat.initparam.MyServletRequest;
import com.tomcat.initparam.MyServletResponse;

/**
 * 
 * function: 自定义容器Servlet父类
 * @author zhanghaolin
 * @date 2018年7月25日
 */
public abstract class MyServlet {

	public void service(MyServletRequest request, MyServletResponse response) {
		if ("GET".equalsIgnoreCase(request.getMethod())) {
			doGet(request, response);
		} else {
			doPost(request, response);
		}
	}
	
	public abstract void doGet(MyServletRequest request, MyServletResponse response);
	
	public abstract void doPost(MyServletRequest request, MyServletResponse response);
	
}

自己业务servlet 如下:

代码语言:javascript复制
package com.haolin.yewu;

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

import com.tomcat.initparam.MyServletRequest;
import com.tomcat.initparam.MyServletResponse;
import com.tomcat.servlet.MyServlet;

/**
 * 
 * function: 业务工程
 * @author zhanghaolin
 * @date 2018年7月25日
 */
public class WodeServlet extends MyServlet {

	@Override
	public void doGet(MyServletRequest request, MyServletResponse response) {
		try {
			StringBuilder builder = new StringBuilder();
			builder.append(MyServletResponse.RESPONSE_HEADER);
			builder.append("--->Url: "   request.getUrl());
			builder.append(";--->t Method: "   request.getMethod());
			String params = "";
			if (null != request.getParamArray() && request.getParamArray().length > 0) {
				String[] paramArray = request.getParamArray();
				for (int i = 0; i < paramArray.length; i  ) {
					params  = paramArray[i]   ",";
				}
				builder.append(";--->t Params: << "   params.substring(0, params.length()-1)   " >>");
			}
			OutputStream outputStream = response.getOutputStream();
			outputStream.write(builder.toString().getBytes("UTF-8"));
			outputStream.flush();
			outputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void doPost(MyServletRequest request, MyServletResponse response) {
		doGet(request, response);
	}

}

OK ,通过启动类启动服务。

效果如下:

启动加载过程:

① 访问不存在的 URL:

② 找到服务:

③ 自定义日志:

努力改变自己和身边人的生活。

特别希望本文可以对你有所帮助,原创不易,感谢你留个赞和关注,道阻且长,我们并肩前行!

转载请注明出处。感谢大家留言讨论交流。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/159639.html原文链接:https://javaforall.cn

0 人点赞