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 到底干了啥

 

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

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

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

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java学习之SSM框架整合

    Java学习之SSM框架整合0x00前言前面的学习的Spring、SpringMVC和Mybatis框架基本已经学习完了,但是要使用起来,我们需要把这三大框架给整合起来一起使用。0x01

    2021年12月12日
    40
  • IDEA好玩的插件之一Activate-power-mode(去掉Max 0)

    IDEA好玩的插件之一Activate-power-mode(去掉Max 0)下载路径:setting->plugins->搜索Activate-power-mode【install即可】window->Activate-power-mode->combo(不勾选combo即可)

    2022年7月15日
    47
  • 数据库查询优化的一般步骤_sql创建数据库失败

    数据库查询优化的一般步骤_sql创建数据库失败长按识别下方二维码,即可"关注"公众号每天早晨,干货准时奉上!0、序言本文我们来谈谈项目中常用的20条MySQL优化方法,效率至少提高3倍!具体如下:1、使⽤…

    2022年8月21日
    5
  • 用于安装python第三方库的工具是_Python第三方库安装

    用于安装python第三方库的工具是_Python第三方库安装Python 有一个全球社区 在这里 我们可以搜索 Python 第三方库的任何话题 PyPI 的全称是 Python 包指数指 Python 包的指数 它是由 PSF Python 软件基金会 和显示全球 Python 计算生态系统 我们需要学会使用 PyPI 的主要网站 搜索和发现我们使用第三方 Python 库和关心 例如 如果您正在开发一个 blockchain related 程序 您需要使用 Python 的计算生态三个步

    2025年7月3日
    1
  • saga分布式事务_本地事务和分布式事务

    saga分布式事务_本地事务和分布式事务在分布式系统中一次操作需要由多个服务协同完成,这种由不同的服务之间通过网络协同完成的事务称为分布式事务。本文详解介绍七种常见分布式事务的原理以及优缺点和适用场景(2PC、3PC、TCC、Saga、本地事务表、MQ事务消息、最大努力通知)

    2022年9月13日
    0
  • Python netcdf_python处理nc文件

    Python netcdf_python处理nc文件  NetCDF(networkCommonDataForm)网络通用数据格式是一种面向数组型并适于网络共享的数据的描述和编码标准。目前,NetCDF广泛应用于大气科学、水文、海洋学、环境模拟、地球物理等诸多领域。用户可以借助多种方式方便地管理和操作NetCDF数据集。  文件的后缀是.nc  这里采用python的一个专门用来处理.nc文件的库–netCDF4该库的安装直接:pipinstallnetCDF4这个库玩起来稍微比Pandas复杂一些。下面以全球降水量数据为例进行

    2022年10月22日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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