- Tomcat是什么?
Tomcat 是一个开源的、轻量级的 Servlet 容器,也被称为 Web 服务器,由 Apache 软件基金会的 Jakarta 项目开发,在 Java Web 开发领域应用广泛。
1)Servlet 容器:Servlet 是 Java 语言编写的服务器端程序,Tomcat 负责管理 Servlet 的生命周期,接收客户端的请求,将请求分发给相应的 Servlet 进行处理,并将 Servlet 处理后的结果返回给客户端。
2)Web 服务器:它能够处理 HTTP 请求,支持静态资源(如 HTML、CSS、JavaScript 文件等)的访问,也能动态生成网页内容。
(如下为其图标)
2.Tomcat结构
- bin目录存 tomcat基本命令
- conf目录放置配置文件
- lib目录主要用来存放tomcat运行需要加载的jar包
- logs目录存放主动启动tomcat的日志
- temp目录存放临时文件
- Work目录存放 tomcat编译时产生的文件
- webapps目录存放我们写的web项目
3.手写Tomcat
手写模拟 Tomcat 程序旨在接收客户端的 HTTP 请求,依据请求的 URL 找到对应的 Servlet 进行处理,并将处理结果以 HTTP 响应的形式返回给客户端。程序通过多个类、接口和注解的协同运作来达成这一功能。
4.在IDEA里手写Tomcat模拟程序的步骤:
1)首先在IDEA里建一个新项目,选择Maven项目(Maven 提供了强大的依赖管理功能),然后在src包中的java包中再创建com.qcby软件包。(如下图)
2)在IDEA里写一个tomcat模拟程序,建一个新项目之后首先建了一个com.qcby软件包 然后在com.qcby包中建了Util,webapps两个包和mytomcat,servletconfigmapping两个类,在util中有responseutil,searchclassutil两个类和webservlet两个注解,在在webapps包中还有三个软件包分别是myweb,req和servlet,myweb中有两个类loginservlet和showservlet,req包中有两个httpservletrequest和httpservletresponse类,在servlet包中有generikservlet,httpservlet两个类和一个servlet接口。(如下图)
5.类、接口和注解的详细信息及关系
1. 注解:WebServlet
- 定义位置:com.qcby.Utill.WebServlet
- 作用:用于标记一个类为 Servlet 类,并指定该 Servlet 映射的 URL 路径。此为自定义注解,模仿了 Java Servlet 规范中的@WebServlet注解。
- 与其他组件的关系:被ServletConfigMapping类用于扫描和识别 Servlet 类及其对应的 URL 映射。
- 代码示例
java
package com.qcby.Utill;
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface WebServlet {
String value();}
2. 工具类:SearchClassUtil
- 定义位置:com.qcby.Utill.SearchClassUtil
- 作用:扫描指定包下的所有类,找出使用了WebServlet注解的类。借助 Java 的反射机制和类加载器来实现类的扫描与查找。
- 与其他组件的关系:为ServletConfigMapping类提供服务,帮助其找到所有被注解的 Servlet 类。
- 代码示例
java
package com.qcby.Utill;
import java.io.File;import java.io.IOException;import java.net.URL;import java.util.ArrayList;import java.util.Enumeration;import java.util.List;
public class SearchClassUtil {
public static List<Class<?>> scanClasses(String packageName) throws IOException, ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
File directory = new File(resource.getFile());
if (directory.exists()) {
findClasses(directory, packageName, classes);
}
}
return classes;
}
private static void findClasses(File directory, String packageName, List<Class<?>> classes) throws ClassNotFoundException {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
findClasses(file, packageName + "." + file.getName(), classes);
} else if (file.getName().endsWith(".class")) {
String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
classes.add(Class.forName(className));
}
}
}
}
public static List<Class<?>> findAnnotatedClasses(String packageName) throws IOException, ClassNotFoundException {
List<Class<?>> allClasses = scanClasses(packageName);
List<Class<?>> annotatedClasses = new ArrayList<>();
for (Class<?> clazz : allClasses) {
if (clazz.isAnnotationPresent(WebServlet.class)) {
annotatedClasses.add(clazz);
}
}
return annotatedClasses;
}}
3. 接口:Servlet
- 定义位置:com.qcby.webapps.servlet.Servlet
- 作用:定义 Servlet 的基本生命周期方法,如init(初始化)、service(处理请求)和destroy(销毁)。所有的 Servlet 类都需要实现这个接口。
- 与其他组件的关系:GenericServlet和HttpServlet都实现了这个接口,为具体的 Servlet 类提供统一的规范。
- 代码示例
java
package com.qcby.webapps.servlet;
import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import java.io.IOException;
public interface Servlet {
void init();
void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
void destroy();}
4. 抽象类:GenericServlet
- 定义位置:com.qcby.webapps.servlet.GenericServlet
- 作用:实现了Servlet接口,提供了一些通用的 Servlet 功能和方法的默认实现。它是一个抽象类,为具体的 Servlet 类提供了一个基础框架。
- 与其他组件的关系:继承自Servlet接口,HttpServlet继承自GenericServlet。
- 代码示例
java
package com.qcby.webapps.servlet;
import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import java.io.IOException;
public abstract class GenericServlet implements Servlet {
@Override
public void init() {
System.out.println("初始化servlet....");
}
@Override
public abstract void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
@Override
public void destroy() {
System.out.println("实现servlet对象的销毁....");
}}
5. 具体类:HttpServlet
- 定义位置:com.qcby.webapps.servlet.HttpServlet
- 作用:继承自GenericServlet,专门用于处理 HTTP 请求。它可以根据不同的 HTTP 请求方法(如 GET、POST)进行不同的处理。
- 与其他组件的关系:继承自GenericServlet,具体的 Servlet 类(如LoginServlet和ShowServlet)可以继承HttpServlet类来处理 HTTP 请求。
- 代码示例
java
package com.qcby.webapps.servlet;
import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import java.io.IOException;
public abstract class HttpServlet extends GenericServlet {
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
String method = request.getMethod();
if ("GET".equalsIgnoreCase(method)) {
doGet(request, response);
} else if ("POST".equalsIgnoreCase(method)) {
doPost(request, response);
}
}
protected abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException;
protected abstract void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException;}
6. 请求和响应类:HttpServletRequest 和 HttpServletResponse
- 定义位置:com.qcby.webapps.req.HttpServletRequest 和 com.qcby.webapps.req.HttpServletResponse
- 作用:HttpServletRequest封装了客户端的 HTTP 请求信息,如请求方法、请求路径、请求参数等;HttpServletResponse封装了服务器的响应信息,用于向客户端返回响应内容。
- 与其他组件的关系:在Servlet接口的service方法中作为参数传递,供具体的 Servlet 类处理请求和生成响应。
- 代码示例
java
// HttpServletRequestpackage com.qcby.webapps.req;
import java.util.HashMap;import java.util.Map;
public class HttpServletRequest {
private String path;
private String method;
private Map<String, String> parameters = new HashMap<>();
public HttpServletRequest() {
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public void setParameter(String name, String value) {
parameters.put(name, value);
}
public String getParameter(String name) {
return parameters.get(name);
}
public void parseParameters(String requestBody) {
if (requestBody != null && !requestBody.isEmpty()) {
String[] pairs = requestBody.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2) {
setParameter(keyValue[0], keyValue[1]);
}
}
}
}}
// HttpServletResponsepackage com.qcby.webapps.req;
import java.io.IOException;import java.io.OutputStream;
public class HttpServletResponse {
private OutputStream outputStream;
public HttpServletResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void sendResponse(String s) throws IOException {
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: " + s.length() + "\r\n" +
"\r\n" +
s;
outputStream.write(response.getBytes());
outputStream.flush();
}}
7. 配置管理类:ServletConfigMapping
- 定义位置:com.qcby.ServletConfigMapping
- 作用:管理 Servlet 的配置信息,将 Servlet 类和其映射的 URL 路径进行关联。它利用SearchClassUtil扫描指定包下的 Servlet 类,并根据WebServlet注解的信息建立映射关系。
- 与其他组件的关系:依赖SearchClassUtil来获取 URL 路径被注解的 Servlet 类,依赖WebServlet注解来获取 URL 路径,为MyTomcat提供 Servlet 映射信息。
- 代码示例
java
package com.qcby;
import com.qcby.Utill.SearchClassUtil;import com.qcby.Utill.WebServlet;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.List;import java.util.Map;
public class ServletConfigMapping {
public static Map<String, HttpServlet> servletMap = new HashMap<>();
public static void init(String packageName) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
List<Class<?>> annotatedClasses = SearchClassUtil.findAnnotatedClasses(packageName);
for (Class<?> clazz : annotatedClasses) {
WebServlet webServlet = clazz.getAnnotation(WebServlet.class);
if (webServlet != null) {
String urlPattern = webServlet.value();
HttpServlet servlet = (HttpServlet) clazz.getDeclaredConstructor().newInstance();
servletMap.put(urlPattern, servlet);
}
}
}}
8. 具体 Servlet 类:LoginServlet 和 ShowServlet
- 定义位置:com.qcby.webapps.myweb.LoginServlet 和 com.qcby.webapps.myweb.ShowServlet
- 作用:具体的业务 Servlet 类,继承自HttpServlet,实现具体的业务逻辑。
- 与其他组件的关系:继承自HttpServlet,通过WebServlet注解的 URL 路径与特定的 URL 进行映射,当客户端请求该 URL 时,对应的 Servlet 类的service方法会被调用进行处理。
- 代码示例
java
// LoginServletpackage com.qcby.webapps.myweb;
import com.qcby.Utill.WebServlet;import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;
@WebServlet("/login")public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
response.sendResponse("<html><body><h1>Hello from LoginServlet (GET)</h1></body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
response.sendResponse("<html><body><h1>Hello from LoginServlet (POST)</h1></body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}}
// ShowServletpackage com.qcby.webapps.myweb;
import com.qcby.Utill.WebServlet;import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;
@WebServlet("/show")public class ShowServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
response.sendResponse("<html><body><h1>Hello from ShowServlet (GET)</h1></body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
response.sendResponse("<html><body><h1>Hello from ShowServlet (POST)</h1></body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}}
9. 核心类:MyTomcat
- 定义位置:com.qcby.MyTomcat
- 作用:模拟 Tomcat 服务器的核心类,包括启动服务器、接收客户端请求、根据请求的 URL 路径从ServletConfigMapping中找到对应的 Servlet,并调用该 Servlet 的service方法进行请求处理和响应生成。
- 与其他组件的关系:依赖ServletConfigMapping类获取 Servlet 映射信息,接收HttpServletRequest和HttpServletResponse对象进行请求处理和响应返回。
- 代码示例
java
package com.qcby;
import com.qcby.webapps.req.HttpServletRequest;import com.qcby.webapps.req.HttpServletResponse;import com.qcby.webapps.servlet.HttpServlet;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;
public class MyTomcat {
public static void main(String[] args) {
try {
ServletConfigMapping.init("com.qcby.webapps.myweb");
ServerSocket serverSocket = new ServerSocket(8081);
System.out.println("MyTomcat started on port 8081");
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
int count = 0;
while (count == 0) {
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);
String context = new String(bytes);
System.out.println(context);
if (context.equals("")) {
System.out.println("你输入了一个空请求");
} else {
String[] lines = context.split("\\r\\n");
String firstLine = lines[0];
String[] parts = firstLine.split(" ");
String method = parts[0];
String uri = parts[1];
HttpServletRequest request = new HttpServletRequest();
request.setPath(uri);
request.setMethod(method);
String requestBody = null;
boolean isBody = false;
StringBuilder bodyBuilder = new StringBuilder();
for (String line : lines) {
if (isBody) {
bodyBuilder.append(line);
}
if (line.isEmpty()) {
isBody = true;
}
}
if (bodyBuilder.length() > 0) {
requestBody = bodyBuilder.toString();
request.parseParameters(requestBody);
}
HttpServletResponse response = new HttpServletResponse(socket.getOutputStream());
if (ServletConfigMapping.servletMap.containsKey(request.getPath())) {
HttpServlet servlet = ServletConfigMapping.servletMap.get(request.getPath());
servlet.service(request, response);
} else {
try {
response.sendResponse("<html><body><h1>404 Not Found</h1></body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}}
关系总结
- GenericServlet实现了Servlet接口。
- HttpServlet继承自GenericServlet。
- LoginServlet和ShowServlet继承自HttpServlet。
- ServletConfigMapping依赖SearchClassUtil查找 Servlet 类,依赖WebServlet注解获取 URL 映射。
- MyTomcat依赖ServletConfigMapping获取 Servlet 映射关系,接收HttpServletRequest和HttpServletResponse进行请求处理和响应。
- WebServlet注解用于LoginServlet和ShowServlet,实现 URL 与 Servlet 类的映射 。
6.
Tomcat 虚拟机工作原理
这个模拟 Tomcat 程序的工作原理可以概括为以下几个步骤:
- 启动服务器:MyTomcat 类的 main 方法创建一个 ServerSocket 对象,并监听指定的端口(这里是 8081)。
- 初始化 Servlet 映射:调用 ServletConfigMapping.init 方法,通过 SearchClassUtil 扫描指定包下的类,找出带有 WebServlet 注解的类,将注解中的 value 属性作为 URL 路径,创建对应的 Servlet 实例,并将 URL 路径和 Servlet 实例的映射关系存储在 servletMap 中。
- 接受客户端请求:ServerSocket 持续监听客户端的连接请求,当有客户端连接时,通过 accept 方法获取一个 Socket 对象,从该对象的输入流中读取客户端发送的 HTTP 请求信息。
- 解析请求信息:将读取到的请求信息解析为 HttpServletRequest 对象,提取请求方法(如 GET、POST)和请求路径。
- 查找对应的 Servlet:根据请求路径在 servletMap 中查找对应的 Servlet 实例。
- 处理请求:如果找到对应的 Servlet 实例,调用其 service 方法处理请求。HttpServlet 类的 service 方法会根据请求方法调用 doGet 或 doPost 方法。
- 发送响应:在 doGet 或 doPost 方法中,根据业务逻辑生成响应内容,通过 HttpServletResponse 对象将响应信息发送给客户端。
- 关闭连接:处理完请求后,关闭 Socket 连接,继续监听下一个客户端请求。
类之间的逻辑关系、映射和继承关系
继承关系
- GenericServlet 实现了 Servlet 接口,提供了 init 和 destroy 方法的默认实现,service 方法为抽象方法,需要子类实现。
- HttpServlet 继承自 GenericServlet,重写了 service 方法,根据请求方法调用 doGet 或 doPost 方法,doGet 和 doPost 方法为抽象方法,需要具体的 Servlet 类实现。
- LoginServlet 和 ShowServlet 继承自 HttpServlet,实现了 doGet 和 doPost 方法,处理具体的业务逻辑。
映射关系
- WebServlet 注解用于将 Servlet 类映射到一个 URL 路径,ServletConfigMapping 类通过扫描带有该注解的类,将 URL 路径和 Servlet 实例的映射关系存储在 servletMap 中。
逻辑关系
- MyTomcat 类是程序的入口,负责启动服务器、接受客户端请求、解析请求信息、查找对应的 Servlet 实例并调用其 service 方法处理请求。
- ServletConfigMapping 类负责初始化 Servlet 映射,通过 SearchClassUtil 扫描指定包下的类,找出带有 WebServlet 注解的类,创建对应的 Servlet 实例,并将 URL 路径和 Servlet 实例的映射关系存储在 servletMap 中。
- SearchClassUtil 类用于扫描指定包下的类,找出带有 WebServlet 注解的类。
- HttpServletRequest 类用于封装客户端的请求信息,包括请求方法、请求路径和请求参数。
- HttpServletResponse 类用于封装服务器的响应信息,提供了 sendResponse 方法将响应信息发送给客户端。
- ResponseUtil 类提供了一些工具方法,用于生成 HTTP 响应头信息。
7.在tomcat中servlet是动态资源;网页前端代码是静态资源
手写完tomcat之后需要配置动态资源和静态资源,才能收到请求
这里以收到servlet的动态资源为重点
配置完动态资源之后在网页搜索localhost://(在MyTomcat定义的端口)8081/show 就会在网页得到请求(如下)
这种情况表明动态资源配置成功了