Tomcat 到底干了啥

Tomcat 到底干了啥Tomcat到底干了啥

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

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

此文为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,如:

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

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();
				}
			}
		}
	}
}
<?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内核处理器:

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 + "\r\n";
			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

如下:

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;
	}

}
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 \r\n"
            + "Content-Type: text/html\r\n"
            + "\r\n";
	
	public MyServletResponse(OutputStream outputStream) {
		this.outputStream = outputStream;
	}

	public OutputStream getOutputStream() {
		return outputStream;
	}
	
}

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

自定义 Tomcat servlet超级父类:

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 如下:

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 ,通过启动类启动服务。

效果如下:

启动加载过程:

by zhanghaolin

① 访问不存在的 URL:

Tomcat 到底干了啥

② 找到服务:

Tomcat 到底干了啥

③ 自定义日志:

Tomcat 到底干了啥

 

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Docker 启动redis镜像

    Docker 启动redis镜像查看本地所有容器:dockerps-a查看本地运行中的容器:dockerps查看所有镜像:dockerimagesdaocker启动redis镜像同时设置密码:dockerrun-d–nameredis1-p6379:6379redis–requirepass“gs123456”参数解释:-d:后台运行-name为容器的名字-p端口映射–requirepass:redis密码redis为镜像名…

  • 1、java语言概述

    1、java语言概述文章目录Java基础知识图解软件开发软件开发人机交互方式命令行常用的DOS命令常用快捷键java语言java版本历史迭代Java技术体系平台Java在各领域的应用Java语言运行机制及运行过程Java语言的特点跨平台性Java两种核心机制核心机制—Java虚拟机核心机制—垃圾回收Java语言的环境搭建什么是JDK,JRE下载并安装JDK配置环境变量开发HelloWorld开发HelloWorld注意:注释(Comment)文档注释Java基础知识图解软件开发软件开发软件,即一系列按照特定顺序组织的

  • Java 二维数组转换成List<List<String>>[通俗易懂]

    Java 二维数组转换成List<List<String>>[通俗易懂]在拿到二维数组的数据时,需要将它转换成嵌套的list,如下工具类:importjava.util.ArrayList;importjava.util.List;/***将二维数组转换成List>形式工具**@authorqiulinhe**2017年3月1日下午3:09:52*/publicclassArrayToListTe

  • 2021年美赛A题思路与程序–已更新

    2021年美赛A题思路与程序–已更新美赛第一小题部分程序%预测的菌群的数量clc;clearall;y=input(‘请输入数据’);n=length(y);yy=ones(n,1);yy(1)=y(1);fori=2:nyy(i)=yy(i-1)+y(i)endB=ones(n-1,2);fori=1:(n-1)B(i,1)=-(yy(i)+yy(i+1))/2;B(i,2)=1;endBT=B’;forj=1:(n-1)YN(j)=y(j+1);endYN=YN’;A=inv(BT

  • windows定时任务schtasks命令详细解

    SCHTASKS/Create[/Ssystem[/Uusername[/P[password]]]][/RUusername[/RPpassword]]/SCschedule[/MOmodifier][/Dday][/Mmonths][/Iidletime]/TNtaskname/TRtaskrun[/STstarttim…

  • laravel 定义翻译字符串

    laravel 定义翻译字符串

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号