当前位置: 首页 > news >正文

Java 编程实战:如何用 Java 编写一个简单而强大的 Tomcat

学习完了JavaWeb,为了深入了解tomcat,打算手撕tomcat搭建自己的tomcat,希望对来访小伙伴也有帮助

引言

        Tomcat 是一个开源的 Web 服务器和 Servlet 容器,它可以提供动态 Web 内容的处理和交互功能。Tomcat 是用 Java 语言编写的,需要运行在 Java 虚拟机上,所以它可以跨平台运行,并且可以与其他 Java 技术集成。Tomcat 是 Java EE 规范的一个实现,它支持 Servlet、JSP、EL、JSTL 等标准技术,以及 Struts、Spring、Hibernate 等流行框架。

        Tomcat 的设计和实现是基于模块化和可扩展的原则,它由多个组件构成,每个组件都有自己的功能和职责。Tomcat 的核心组件是 Catalina,它是一个 Servlet 容器,负责管理和执行 Servlet。其他组件包括 Coyote,它是一个连接器,负责接收和解析 HTTP 请求;Jasper,它是一个 JSP 引擎,负责编译和执行 JSP 页面;Cluster,它是一个集群模块,负责实现负载均衡和会话复制等功能;以及其他一些辅助组件,如安全模块、日志模块、管理模块等。

        本文将介绍如何使用 Java 编写 Tomcat,并实现一个简单的 Web 服务器和 Servlet 容器。本文还将介绍 Tomcat 的基本框架和相关配置,并使用 Eclipse 进行开发和调试。本文旨在帮助读者理解和掌握 Tomcat 的原理和用法。

目录

(一)创建项目

(二)编写代码

1.Server

2.Connector

3.request

4.response

5.Processor

6.StaticProcessor

7.DynamicProcessor

8.ServletContainer

9.Servlet

10.HelloServlet

(三)编写配置文件

(四)编写 Web 应用

(五)测试和调试

(六)总结


使用 Java 编写 Tomcat

(一)创建项目

首先,我们需要创建一个 Java 项目,并命名为 MyTomcat。我们可以使用 Eclipse 或者其他 IDE 来创建项目,也可以使用命令行或者文本编辑器来创建项目。在本文中,我们使用 Eclipse 作为开发工具。

在 Eclipse 中,我们选择 File -> New -> Java Project,然后输入项目名称 MyTomcat,并选择 JDK 作为 JRE System Library。点击 Finish 完成项目的创建。

        我们创建的项目的结构,如下:有一个 src 文件夹,用于存放源代码文件;有一个 bin 文件夹,用于存放编译后的字节码文件;有一个 lib 文件夹,用于存放依赖的 jar 包;有一个 webapps 文件夹,用于存放 Web 应用;有一个 conf 文件夹,用于存放配置文件;有一个 logs 文件夹,用于存放日志文件。

(二)编写代码

        接下来,我们需要编写代码来实现 Tomcat 的功能。我们需要实现以下几个类:

  • Server:这是 Tomcat 的主类,负责启动 Tomcat 服务器,并初始化各个组件。
  • Connector:这是连接器类,负责接收客户端的 HTTP 请求,并将其封装成 Request 对象。
  • Request:这是请求类,负责存储请求的相关信息,如请求方法、请求路径、请求参数等。
  • Response:这是响应类,负责存储响应的相关信息,如响应状态码、响应头、响应体等。
  • Processor:这是处理器类,负责根据请求的类型(静态或动态)来选择不同的处理方式,并将结果写入 Response 对象。
  • StaticProcessor:这是静态处理器类,负责处理静态资源的请求,如 HTML、CSS、JS、图片等。
  • DynamicProcessor:这是动态处理器类,负责处理动态资源的请求,如 Servlet、JSP 等。
  • ServletContainer:这是 Servlet 容器类,负责管理和执行 Servlet。
  • Servlet:这是一个接口,定义了 Servlet 的规范,所有的 Servlet 都必须实现这个接口。
  • HelloServlet:这是一个具体的 Servlet 类,用于演示 Tomcat 的功能。

        下面逐一介绍这些类的代码和功能。

1.Server

        Server 类是 Tomcat 的主类,它有一个 main 方法,用于启动 Tomcat 服务器,并初始化各个组件。它有一个 ServerSocket 属性,用于监听客户端的连接请求。它有一个 Connector 属性,用于创建和管理连接器。它有一个 port 属性,用于指定服务器的监听端口。它有一个 webapps 属性,用于指定 Web 应用的根目录。它有一个 conf 属性,用于指定配置文件的路径。它有一个 servletContainer 属性,用于创建和管理 Servlet 容器。

        Server 类的代码如下:

package com.mytomcat;import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;/*** Tomcat 的主类,负责启动服务器,并初始化各个组件*/
public class Server {// 服务器套接字private ServerSocket serverSocket;// 连接器private Connector connector;// 服务器监听端口private int port = 8080;// Web 应用根目录private String webapps = "webapps";// 配置文件路径private String conf = "conf/web.xml";// Servlet 容器private ServletContainer servletContainer;/*** 构造方法,初始化各个组件*/public Server() {try {// 创建服务器套接字,并绑定端口serverSocket = new ServerSocket(port);System.out.println("Server started at port: " + port);// 创建连接器connector = new Connector();// 创建 Servlet 容器,并加载配置文件servletContainer = new ServletContainer(new File(conf));} catch (IOException e) {e.printStackTrace();}}/*** 启动服务器的方法*/public void start() {while (true) {try {// 接受客户端的连接请求,返回一个套接字Socket socket = serverSocket.accept();System.out.println("Client connected: " + socket.getInetAddress());// 交给连接器处理connector.process(socket, webapps, servletContainer);} catch (IOException e) {e.printStackTrace();}}}/*** 主方法,创建并启动服务器实例* @param args 命令行参数*/public static void main(String[] args) {Server server = new Server();server.start();}
}

2.Connector

        Connector 类是连接器类,它负责接收客户端的 HTTP 请求,并将其封装成 Request 对象。它有一个 process 方法,用于处理客户端的连接请求。它有一个 Processor 属性,用于创建和管理处理器。

        Connector 类的代码如下:

package com.mytomcat;import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;/*** 连接器类,负责接收客户端的 HTTP 请求,并将其封装成 Request 对象*/
public class Connector {// 处理器private Processor processor;/*** 构造方法,初始化处理器*/public Connector() {processor = new Processor();}/*** 处理客户端连接请求的方法* @param socket 套接字* @param webapps Web 应用根目录* @param servletContainer Servlet 容器*/public void process(Socket socket, String webapps, ServletContainer servletContainer) {try {// 获取输入流,读取请求内容InputStream inputStream = socket.getInputStream();// 创建请求对象,并解析请求内容Request request = new Request(inputStream);// 创建响应对象,并关联套接字的输出流Response response = new Response(socket.getOutputStream());// 交给处理器处理processor.process(request, response, webapps, servletContainer);} catch (IOException e) {e.printStackTrace();} finally {// 关闭套接字try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}

3.request

        Request 类是请求类,负责存储请求的相关信息,如请求方法、请求路径、请求参数等。它有一个构造方法,用于接收输入流,并解析请求内容。它有一些属性和方法,用于获取和设置请求的相关信息。

        Request 类的代码如下:

package com.mytomcat;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;/*** 请求类,负责存储请求的相关信息,如请求方法、请求路径、请求参数等*/
public class Request {// 输入流private InputStream inputStream;// 请求方法private String method;// 请求路径private String uri;// 请求参数private Map<String, String> parameters;/*** 构造方法,接收输入流,并解析请求内容* @param inputStream 输入流*/public Request(InputStream inputStream) {this.inputStream = inputStream;// 创建参数映射对象parameters = new HashMap<>();// 解析请求内容parse();}/*** 解析请求内容的方法*/private void parse() {try {// 创建缓冲读取器,读取输入流中的内容BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));// 读取第一行内容,即请求行String requestLine = br.readLine();System.out.println("Request Line: " + requestLine);// 如果请求行不为空,则继续解析if (requestLine != null) {// 将请求行按空格分割成三部分,分别是请求方法、请求路径和协议版本String[] parts = requestLine.split(" ");// 获取并设置请求方法method = parts[0];// 获取并设置请求路径uri = parts[1];// 如果请求路径中包含 ? ,则表示有查询字符串,需要进一步解析if (uri.contains("?")) {// 将请求路径按 ? 分割成两部分,分别是路径和查询字符串parts = uri.split("\\?");// 重新设置请求路径为 ? 前面的部分uri = parts[0];// 获取查询字符串String queryString = parts[1];// 如果查询字符串不为空,则继续解析if (queryString != null && !queryString.isEmpty()) {// 将查询字符串按 & 分割成多个键值对parts = queryString.split("&");// 遍历每个键值对for (String part : parts) {// 将键值对按 = 分割成两部分,分别是键和值String[] pair = part.split("=");// 获取并设置参数的键和值,并放入参数映射对象中parameters.put(pair[0], pair[1]);}}}}} catch (IOException e) {e.printStackTrace();}}/*** 获取输入流的方法* @return 输入流对象*/public InputStream getInputStream() {return inputStream;}/*** 获取请求方法的方法* @return 请求方法字符串*/public String getMethod() {return method;}/*** 获取请求路径的方法* @return 请求路径字符串*/public String getUri() {return uri;}/*** 根据参数名获取参数值的方法* @param name 参数名字符串* @return 参数值字符串,如果没有找到,则返回 null*/public String getParameter(String name) {return parameters.get(name);}
}

4.response

        Response 类是响应类,负责存储响应的相关信息,如响应状态码、响应头、响应体等。它有一个构造方法,用于接收输出流,并初始化响应内容。它有一些属性和方法,用于获取和设置响应的相关信息。它还有一个 send 方法,用于发送响应内容到输出流中。

        Request 类的代码如下:

package com.mytomcat;import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;/*** 响应类,负责存储响应的相关信息,如响应状态码、响应头、响应体等*/
public class Response {// 输出流private OutputStream outputStream;// 响应状态码private int status;// 响应头private Map<String, String> headers;// 响应体private StringBuilder body;// 缓冲写入器private PrintWriter writer;/*** 构造方法,接收输出流,并初始化响应内容* @param outputStream 输出流*/public Response(OutputStream outputStream) {this.outputStream = outputStream;// 创建响应头映射对象headers = new HashMap<>();// 创建响应体字符串对象body = new StringBuilder();// 创建缓冲写入器,关联响应体字符串对象writer = new PrintWriter(body);}/*** 获取输出流的方法* @return 输出流对象*/public OutputStream getOutputStream() {return outputStream;}/*** 获取缓冲写入器的方法* @return 缓冲写入器对象*/public PrintWriter getWriter() {return writer;}/*** 设置响应状态码的方法* @param status 响应状态码整数*/public void setStatus(int status) {this.status = status;}/*** 设置响应头的方法* @param name 响应头名字符串* @param value 响应头值字符串*/public void setHeader(String name, String value) {headers.put(name, value);}/*** 设置响应头 Content-Type 的方法* @param contentType Content-Type 字符串*/public void setContentType(String contentType) {setHeader("Content-Type", contentType);}/*** 设置响应头 Content-Length 的方法* @param contentLength Content-Length 整数*/public void setContentLength(int contentLength) {setHeader("Content-Length", String.valueOf(contentLength));}/*** 向响应体中写入字符串的方法* @param s 字符串对象*/public void println(String s) {writer.println(s);}/*** 向响应体中写入字节数组的方法* @param b 字节数组对象* @param off 起始位置整数* @param len 长度整数*/public void write(byte[] b, int off, int len) {writer.write(new String(b, off, len));}

5.Processor

        Processor 类是处理器类,它负责根据请求的类型(静态或动态)来选择不同的处理方式,并将结果写入 Response 对象。它有一个 process 方法,用于处理请求和响应。它有一个 StaticProcessor 属性,用于创建和管理静态处理器。它有一个 DynamicProcessor 属性,用于创建和管理动态处理器。

        Processor 类的代码如下:

package com.mytomcat;/*** 处理器类,负责根据请求的类型(静态或动态)来选择不同的处理方式,并将结果写入 Response 对象*/
public class Processor {// 静态处理器private StaticProcessor staticProcessor;// 动态处理器private DynamicProcessor dynamicProcessor;/*** 构造方法,初始化静态处理器和动态处理器*/public Processor() {staticProcessor = new StaticProcessor();dynamicProcessor = new DynamicProcessor();}/*** 处理请求和响应的方法* @param request 请求对象* @param response 响应对象* @param webapps Web 应用根目录* @param servletContainer Servlet 容器*/public void process(Request request, Response response, String webapps, ServletContainer servletContainer) {// 获取请求路径String uri = request.getUri();// 判断请求路径是否以 /servlet/ 开头,如果是,则表示请求动态资源,否则表示请求静态资源if (uri.startsWith("/servlet/")) {// 交给动态处理器处理dynamicProcessor.process(request, response, servletContainer);} else {// 交给静态处理器处理staticProcessor.process(request, response, webapps);}}
}

6.StaticProcessor

        StaticProcessor 类是静态处理器类,它负责处理静态资源的请求,如 HTML、CSS、JS、图片等。它有一个 process 方法,用于读取静态资源文件,并将其内容写入 Response 对象。

        StaticProcessor 类的代码如下:

package com.mytomcat;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;/*** 静态处理器类,负责处理静态资源的请求,如 HTML、CSS、JS、图片等*/
public class StaticProcessor {/*** 处理静态资源请求的方法* @param request 请求对象* @param response 响应对象* @param webapps Web 应用根目录*/public void process(Request request, Response response, String webapps) {try {// 获取请求路径String uri = request.getUri();// 根据 Web 应用根目录和请求路径构造文件路径String filePath = webapps + uri;// 创建文件对象File file = new File(filePath);// 判断文件是否存在且可读,如果是,则表示找到了对应的静态资源,否则表示没有找到对应的静态资源if (file.exists() && file.canRead()) {// 设置响应状态码为 200 OKresponse.setStatus(200);// 设置响应头 Content-Type 为根据文件扩展名判断的 MIME 类型response.setContentType(getContentType(file));// 设置响应头 Content-Length 为文件长度response.setContentLength((int) file.length());// 创建文件输入流,读取文件内容FileInputStream fis = new FileInputStream(file);byte[] buffer = new byte[1024];int len = 0;while ((len = fis.read(buffer)) != -1) {// 将文件内容写入响应体中response.write(buffer, 0, len);}// 关闭文件输入流fis.close();} else {// 设置响应状态码为 404 Not Foundresponse.setStatus(404);// 设置响应头 Content-Type 为 text/htmlresponse.setContentType("text/html");// 设置响应体为一个简单的错误页面response.println("<html><head><title>404 Not Found</title></head><body>");response.println("<h1>404 Not Found</h1>");response.println("<p>The requested resource " + uri + " was not found on this server.</p>");response.println("</body></html>");}// 发送响应response.send();} catch (IOException e) {e.printStackTrace();}}/*** 根据文件扩展名判断 MIME 类型的方法* @param file 文件对象* @return MIME 类型字符串*/private String getContentType(File file) {// 获取文件名String fileName = file.getName();// 获取文件扩展名String extension = fileName.substring(fileName.lastIndexOf(".") + 1);// 根据文件扩展名判断 MIME 类型,这里只列举了一些常见的类型,实际上还有很多其他的类型switch (extension) {case "html":case "htm":return "text/html";case "css":return "text/css";case "js":return "text/javascript";case "jpg":case "jpeg":return "image/jpeg";case "png":return "image/png";case "gif":return "image/gif";default:return "application/octet-stream";}}
}

7.DynamicProcessor

        DynamicProcessor 类是动态处理器类,它负责处理动态资源的请求,如 Servlet、JSP 等。它有一个 process 方法,用于根据请求路径找到对应的 Servlet,并调用其 service 方法,并将结果写入 Response 对象。

        DynamicProcessor 类的代码如下:

package com.mytomcat;/*** 动态处理器类,负责处理动态资源的请求,如 Servlet、JSP 等*/
public class DynamicProcessor {/*** 处理动态资源请求的方法* @param request 请求对象* @param response 响应对象* @param servletContainer Servlet 容器*/public void process(Request request, Response response, ServletContainer servletContainer) {try {// 获取请求路径String uri = request.getUri();// 根据请求路径从 Servlet 容器中获取对应的 Servlet 实例,如果没有找到,则返回 nullServlet servlet = servletContainer.getServlet(uri);// 判断是否找到了对应的 Servlet,如果是,则表示找到了对应的动态资源,否则表示没有找到对应的动态资源if (servlet != null) {// 设置响应状态码为 200 OKresponse.setStatus(200);// 调用 Servlet 的 service 方法,传入请求对象和响应对象,让 Servlet 处理业务逻辑,并生成响应内容servlet.service(request, response);} else {// 设置响应状态码为 404 Not Foundresponse.setStatus(404);// 设置响应头 Content-Type 为 text/htmlresponse.setContentType("text/html");// 设置响应体为一个简单的错误页面response.println("<html><head><title>404 Not Found</title></head><body>");response.println("<h1>404 Not Found</h1>");response.println("<p>The requested resource " + uri + " was not found on this server.</p>");response.println("</body></html>");}// 发送响应response.send();} catch (Exception e) {e.printStackTrace();}}
}

8.ServletContainer

        ServletContainer 类是 Servlet 容器类,它负责管理和执行 Servlet。它有一个 load 方法,用于加载配置文件,并根据配置文件中的信息创建和注册 Servlet 实例。它有一个 getServlet 方法,用于根据请求路径获取对应的 Servlet 实例。它有一个 Map 属性,用于存储请求路径和 Servlet 实例之间的映射关系。

        ServletContainer 类的代码如下:

package com.mytomcat;import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;/*** Servlet 容器类,负责管理和执行 Servlet*/
public class ServletContainer {// 请求路径和 Servlet 实例之间的映射关系private Map<String, Servlet> servletMap;/*** 构造方法,加载配置文件,并创建和注册 Servlet 实例* @param configFile 配置文件对象*/public ServletContainer(File configFile) {// 创建映射关系对象servletMap = new HashMap<>();// 加载配置文件load(configFile);}/*** 加载配置文件,并创建和注册 Servlet 实例的方法* @param configFile 配置文件对象*/private void load(File configFile) {try {// 创建属性对象,用于存储配置文件中的键值对Properties properties = new Properties();// 创建文件输入流,读取配置文件内容FileInputStream fis = new FileInputStream(configFile);// 加载配置文件内容到属性对象中properties.load(fis);// 关闭文件输入流fis.close();// 遍历属性对象中的所有键值对for (Map.Entry<Object, Object> entry : properties.entrySet()) {// 获取键,即请求路径String uri = (String) entry.getKey();// 获取值,即 Servlet 类名String className = (String) entry.getValue();// 通过反射机制,根据类名创建 Servlet 类的实例Class<?> clazz = Class.forName(className);Servlet servlet = (Servlet) clazz.newInstance();// 将请求路径和 Servlet 实例放入映射关系对象中servletMap.put(uri, servlet);}} catch (Exception e) {e.printStackTrace();}}/*** 根据请求路径获取对应的 Servlet 实例的方法* @param uri 请求路径* @return 对应的 Servlet 实例,如果没有找到,则返回 null*/public Servlet getServlet(String uri) {// 从映射关系对象中根据请求路径获取对应的 Servlet 实例,并返回return servletMap.get(uri);}
}

9.Servlet

        Servlet 是一个接口,定义了 Servlet 的规范,所有的 Servlet 都必须实现这个接口。Servlet 接口有一个 service 方法,用于处理客户端的请求,并生成响应内容。Servlet 接口还有一些其他的方法,如 init、destroy、getServletConfig、getServletInfo 等,但在本文中我们不需要实现这些方法。

        Servlet 接口的代码如下:

package com.mytomcat;import java.io.IOException;/*** Servlet 接口,定义了 Servlet 的规范,所有的 Servlet 都必须实现这个接口*/
public interface Servlet {/*** 处理客户端请求,并生成响应内容的方法* @param request 请求对象* @param response 响应对象* @throws IOException 输入输出异常*/public void service(Request request, Response response) throws IOException;
}

10.HelloServlet

        HelloServlet 是一个具体的 Servlet 类,用于演示 Tomcat 的功能。它实现了 Servlet 接口,并重写了 service 方法。在 service 方法中,它根据请求参数 name 的值,生成一个简单的欢迎页面,并将其写入响应对象中。

        HelloServlet 类的代码如下:

package com.mytomcat;import java.io.IOException;/*** 一个具体的 Servlet 类,用于演示 Tomcat 的功能*/
public class HelloServlet implements Servlet {/*** 处理客户端请求,并生成响应内容的方法* @param request 请求对象* @param response 响应对象* @throws IOException 输入输出异常*/@Overridepublic void service(Request request, Response response) throws IOException {// 获取请求参数 name 的值,如果没有,则默认为 WorldString name = request.getParameter("name");if (name == null) {name = "World";}// 设置响应头 Content-Type 为 text/htmlresponse.setContentType("text/html");// 设置响应体为一个简单的欢迎页面response.println("<html><head><title>Hello Servlet</title></head><body>");response.println("<h1>Hello, " + name + "!</h1>");response.println("<p>This is a simple Servlet example.</p>");response.println("</body></html>");}
}

(三)编写配置文件

        接下来,我们需要编写配置文件,用于指定请求路径和 Servlet 类名之间的映射关系。我们使用一个简单的属性文件(web.xml)来存储这些信息。属性文件中的每一行都是一个键值对,键是请求路径,值是 Servlet 类名。我们需要将属性文件放在 conf 文件夹中。

        属性文件的内容如下:

/servlet/HelloServlet=com.mytomcat.HelloServlet

        这表示当客户端请求 /servlet/HelloServlet 路径时,服务器会调用 com.mytomcat.HelloServlet 类的实例来处理请求。

(四)编写 Web 应用

        最后,我们需要编写 Web 应用,用于测试 Tomcat 的功能。我们使用一个简单的 HTML 文件(index.html)来作为 Web 应用的入口页面。HTML 文件中有一个表单,用于向服务器发送请求,并携带一个 name 参数。我们需要将 HTML 文件放在 webapps 文件夹中。

        HTML 文件的内容如下:

<html>
<head><title>Tomcat Test</title>
</head>
<body><h1>Tomcat Test</h1><p>This is a simple Web application to test Tomcat.</p><form action="/servlet/HelloServlet" method="get"><p>Please enter your name:</p><input type="text" name="name"><input type="submit" value="Submit"></form>
</body>
</html>

        这表示当用户点击提交按钮时,浏览器会向服务器发送一个 GET 请求,并携带一个 name 参数,请求路径为 /servlet/HelloServlet。

(五)测试和调试

        现在,我们已经完成了 Tomcat 的编写和配置,我们可以运行 Server 类的 main 方法来启动 Tomcat 服务器,并在浏览器中访问 http://localhost:8080/index.html 来测试 Tomcat 的功能。

        如果我们遇到了任何问题或错误,我们可以使用 Eclipse 的调试功能来进行调试。我们可以在代码中设置断点,然后使用 Debug As -> Java Application 来运行 Server 类的 main 方法。这样,当程序执行到断点时,Eclipse 会暂停程序的执行,并显示当前的变量值、堆栈信息、控制台输出等信息。我们可以使用 Step Into、Step Over、Step Return 等命令来逐步执行程序,并观察程序的运行情况。

(六)总结

        本文介绍了如何使用 Java 编写 Tomcat,并实现一个简单的 Web 服务器和 Servlet 容器。本文还介绍了 Tomcat 的基本框架和相关配置,并使用 Eclipse 进行开发和调试。本文旨在帮助伙伴们理解和掌握 Tomcat 的原理和用法。

        Tomcat 是一个开源的 Web 服务器和 Servlet 容器,它可以提供动态 Web 内容的处理和交互功能。Tomcat 是用 Java 语言编写的,需要运行在 Java 虚拟机上,所以它可以跨平台运行,并且可以与其他 Java 技术集成。Tomcat 是 Java EE 规范的一个实现,它支持 Servlet、JSP、EL、JSTL 等标准技术,以及 Struts、Spring、Hibernate 等流行框架。

        Tomcat 的设计和实现是基于模块化和可扩展的原则,它由多个组件构成,每个组件都有自己的功能和职责。Tomcat 的核心组件是 Catalina,它是一个 Servlet 容器,负责管理和执行 Servlet。其他组件包括 Coyote,它是一个连接器,负责接收和解析 HTTP 请求;Jasper,它是一个 JSP 引擎,负责编译和执行 JSP 页面;Cluster,它是一个集群模块,负责实现负载均衡和会话复制等功能;以及其他一些辅助组件,如安全模块、日志模块、管理模块等。

以上就是全部内容啦~

相关文章:

Java 编程实战:如何用 Java 编写一个简单而强大的 Tomcat

学习完了JavaWeb&#xff0c;为了深入了解tomcat&#xff0c;打算手撕tomcat搭建自己的tomcat&#xff0c;希望对来访小伙伴也有帮助 引言 Tomcat 是一个开源的 Web 服务器和 Servlet 容器&#xff0c;它可以提供动态 Web 内容的处理和交互功能。Tomcat 是用 Java 语言编写的&a…...

【JavaSE】数组的定义与使用

详解数组 数组的基本概念什么是数组数组的创建及初始化数组的使用 数组是引用类型基本类型变量与引用类型变量的区别引用变量认识 null 数组的应用场景数组练习二维数组 数组的基本概念 什么是数组 数组可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。比如现实…...

银河麒麟安装php7.1.33

银河麒麟V10兼容CentOS 8 安装过程与CentOS类似。 TencentOS3.1安装PHPNginxredis测试系统_乐大师的博客-CSDN博客 可以参考之前我写的文章。 不过有2个细节不同&#xff0c;下面说下。 问题1&#xff1a;编译错误提示“error:off_t undefined” 解决方法&#xff1a; 编…...

Kubernetes集群部署上篇(安装部署,但是集群网络未部署)

第四阶段 时 间&#xff1a;2023年8月9日 参加人&#xff1a;全班人员 内 容&#xff1a; Kubernetes集群部署上篇 目录 一、Kubernetes部署方式 &#xff08;一&#xff09;minikube &#xff08;二&#xff09;二进制包 &#xff08;三&#xff09;Kubeadm Kubea…...

跨境电商中的安全挑战与隐擎Fox指纹浏览器的应用

随着全球互联网的蓬勃发展&#xff0c;跨境电商已经成为了国际贸易的重要组成部分。然而&#xff0c;跨境电商的迅速崛起也伴随着一系列安全挑战&#xff0c;其中之一就是恶意活动和隐私泄露。为了应对这些挑战&#xff0c;诸多技术手段被开发出来&#xff0c;其中隐擎Fox指纹浏…...

10. Docker Swarm(一)

目录 1、前言 2、Docker Swarm体系架构 2.1、简单介绍 2.2、体系架构 3、简单使用 3.1、环境准备 3.2、初始化master节点 3.3、建立worker节点 3.4、查看集群的节点信息 3.5、部署应用 3.5.1、创建Dockerfile文件 3.5.2、构建镜像 3.5.3、将镜像上传到Docker仓库 …...

【MySQL】InnoDB存储引擎详解

InnoDB引擎是MySQL5.5版本之后默认的存储引擎 逻辑存储结构 首先是表空间Tablespace&#xff08;ibd文件&#xff09;&#xff1a;一个mysql实力可以对应多个表空间&#xff0c;用于存储及记录&#xff0c;索引等数据 这些存储记录&#xff0c;索引等数据中是用段(Segment)来…...

组合求和-矩阵连乘所有加括号方式_2023_08_12

矩阵链加括号方式总数 前言 矩阵链乘积的瓶颈在于其标量运算的次数&#xff0c;不同的结合次序对其时间性能影响远大于矩阵乘积运算本身&#xff0c;可以看到许多教材上把求解矩阵标量运算的最优解作为动态规划的示例&#xff0c;问题隐含动态规划两大特征&#xff1a; 最优子…...

《3D 数学基础》12 几何图元

目录 1 表达图元的方法 1.1 隐式表示法 1.2 参数表示 1.3 直接表示 2. 直线和射线 2.1 射线的不同表示法 2.1.1 两点表示 2.1.2 参数表示 2.1.3 相互转换 2.2 直线的不同表示法 2.2.1 隐式表示法 2.2.2 斜截式 2.2.3 相互转换 3. 球 3.1 隐式表示 1 表达图元的方…...

【设计模式——学习笔记】23种设计模式——备忘录模式Memento(原理讲解+应用场景介绍+案例介绍+Java代码实现)

案例引入 游戏角色有攻击力和防御力&#xff0c;在大战Boss前保存自身的状态(攻击力和防御力)&#xff0c;当大战Boss后攻击力和防御力下降&#xff0c;可以从备忘录对象恢复到大战前的状态 传统设计方案 针对每一种角色&#xff0c;设计一个类来存储该角色的状态 【分析】…...

致谢丨感谢有你,JumpServer开源项目九周年致谢名单

2014年到2023年&#xff0c;JumpServer开源项目已经走过了九年的时间。感谢以下社区贡献者对JumpServer项目的帮助和支持。 因为有你&#xff0c;一切才能成真。 JumpServer开源项目贡献者奖杯将于近日邮寄到以上贡献者手中&#xff0c;同时JumpServer开源项目组还准备了一份小…...

使用 Python 和 Flask 构建简单的 Restful API 第 1 部分

一、说明 我将把这个系列分成 3 或 4 篇文章。在本系列的最后&#xff0c;您将了解使用flask构建 restful API 是多么容易。在本文中&#xff0c;我们将设置环境并创建将显示“Hello World”的终结点。 我假设你的电脑上安装了python 2.7和pip。我已经在python 2.7上测试了本文…...

【深度学习所有损失函数】在 NumPy、TensorFlow 和 PyTorch 中实现(2/2)

一、说明 在本文中&#xff0c;讨论了深度学习中使用的所有常见损失函数&#xff0c;并在NumPy&#xff0c;PyTorch和TensorFlow中实现了它们。 (二-五)见 六、稀疏分类交叉熵损失 稀疏分类交叉熵损失类似于分类交叉熵损失&#xff0c;但在真实标签作为整数而不是独热编码提…...

Hazel 引擎学习笔记

目录 Hazel 引擎学习笔记学习方法思考引擎结构创建工程程序入口点日志系统Premake\MD没有 cpp 文件的项目会出错include 到某个库就要包含这个库的路径&#xff0c;注意头文件展开 事件系统 获取和利用派生类信息预编译头文件抽象窗口类和 GLFWgit submodule addpremake 脚本禁…...

Linux系统下Redis3.2集群

本节主要学习reids主从复制的概念&#xff0c;作用&#xff0c;缺点&#xff0c;流程&#xff0c;搭建&#xff0c;验证&#xff0c;reids哨兵模式的概念&#xff0c;作用&#xff0c;缺点&#xff0c;结构&#xff0c;搭建&#xff0c;验证等。 文章目录 一、redis主从复制 …...

Android图形-合成与显示-SurfaceTestDemo

目录 引言&#xff1a; 主程序代码&#xff1a; 结果呈现&#xff1a; 小结&#xff1a; 引言&#xff1a; 通过一个最简单的测试程序直观Android系统的native层Surface的渲染显示过程。 主程序代码&#xff1a; #include <cutils/memory.h> #include <utils/L…...

高压放大器怎么设计(高压放大器设计方案)

高压放大器是一种用于将低电压信号转换成高电压信号的电子设备&#xff0c;广泛应用于通信、雷达、医疗设备等领域。在设计高压放大器时&#xff0c;需要考虑多种因素&#xff0c;如输入输出信号的特性、电路结构的选择、电源和负载匹配等。本文将介绍高压放大器的设计方法和注…...

SpringBoot yml配置注入

yaml语法学习 1、配置文件 SpringBoot使用一个全局的配置文件 &#xff0c; 配置文件名称是固定的 application.properties 语法结构 &#xff1a;keyvalue application.yml 语法结构 &#xff1a;key&#xff1a;空格 value 配置文件的作用&#xff1a;修改SpringBoot自动…...

中科亿海微乘法器(LPMMULT)

引言 FPGA&#xff08;可编程逻辑门阵列&#xff09;是一种可在硬件级别上重新配置的集成电路。它具有灵活性和可重构性&#xff0c;使其成为处理各种应用的理想选择&#xff0c;包括数字信号处理、图像处理、通信、嵌入式系统等。在FPGA中&#xff0c;乘法器是一种重要的硬件资…...

Redis_持久化(AOF、RDB)

6. Redis AOF 6.1 简介 目前&#xff0c;redis的持久化主要应用AOF&#xff08;Append Only File&#xff09;和RDF两大机制&#xff0c;AOF以日志的形式来记录每个写操作&#xff08;增量保存&#xff09;&#xff0c;将redis执行过的所有指令全部安全记录下来&#xff08;读…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...