Tomcat 到底干了啥

Posted 一小页

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 ,通过启动类启动服务。

效果如下:

启动加载过程:

① 访问不存在的 URL:

② 找到服务:

③ 自定义日志:

 

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

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

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

以上是关于Tomcat 到底干了啥的主要内容,如果未能解决你的问题,请参考以下文章

python的import到底干了啥

《JavaScript》探索new运算符到底干了啥

我这半年到底干了啥(附多家详细面经)

一次触摸,Android到底干了啥

Android源码解析View.post()到底干了啥

dubbo中registryroutedirectoryclusterloadbalanceroute的关系以及一个引用操作和调用操作到底干了啥