大家好,又见面了,我是你们的朋友全栈君。
道阻且长,行则将至。请相信我,你一定会更优秀!
此文为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