目录
[2、Servlet 的加载(生命周期)原理](#2、Servlet 的加载(生命周期)原理)
[步骤 1:请求到达服务器](#步骤 1:请求到达服务器)
[步骤 2:Tomcat 通过 Socket 建立连接](#步骤 2:Tomcat 通过 Socket 建立连接)
[步骤 3:解析 HTTP 请求数据](#步骤 3:解析 HTTP 请求数据)
[步骤 4:Servlet 容器匹配对应的 Servlet 对象](#步骤 4:Servlet 容器匹配对应的 Servlet 对象)
[步骤 5:调用 Servlet 的业务方法处理请求](#步骤 5:调用 Servlet 的业务方法处理请求)
[步骤 6:返回响应](#步骤 6:返回响应)
[1. BIO(Blocking IO,阻塞 IO)](#1. BIO(Blocking IO,阻塞 IO))
[2. NIO(Non-Blocking IO,非阻塞 IO)](#2. NIO(Non-Blocking IO,非阻塞 IO))
[3. AIO(Asynchronous IO,异步 IO)](#3. AIO(Asynchronous IO,异步 IO))
[3.1 启动阶段:初始化 Servlet 容器](#3.1 启动阶段:初始化 Servlet 容器)
[3.2 请求处理阶段:接收请求→处理→响应](#3.2 请求处理阶段:接收请求→处理→响应)
[3.3 核心本质](#3.3 核心本质)
[4.1 首先在idea中创建maven项目](#4.1 首先在idea中创建maven项目)
[4.2 我们在使用tomcat的时候,我们会发现我们的servlet会放在webapps文件夹里面,所以我们也创建一个webapps。](#4.2 我们在使用tomcat的时候,我们会发现我们的servlet会放在webapps文件夹里面,所以我们也创建一个webapps。)
[4.3 在webapps里面创建MyfirstServlet和MysecondServlet。](#4.3 在webapps里面创建MyfirstServlet和MysecondServlet。)
[4.4 接着我们回看之前servlet的加载流程,以MyfirstServlet举例,MyfirstServlet继承HttpServlet,HttpServlet里面是继承GenericServlet并重写service()方法,而在GenericServlet里面是实现了Servlet接口里面的除service()方法以外的所有方法。](#4.4 接着我们回看之前servlet的加载流程,以MyfirstServlet举例,MyfirstServlet继承HttpServlet,HttpServlet里面是继承GenericServlet并重写service()方法,而在GenericServlet里面是实现了Servlet接口里面的除service()方法以外的所有方法。)
[4.5 在这里本应该是重写doget、dopost等方法,考虑到实际的业务,这里暂时跳过。](#4.5 在这里本应该是重写doget、dopost等方法,考虑到实际的业务,这里暂时跳过。)
[4.6 这是我们考虑到了第三步@WebServlet注解。](#4.6 这是我们考虑到了第三步@WebServlet注解。)
[4.7 接下来我们需要建立servlet映射表,利用到了反射和hashMap的具体实现。](#4.7 接下来我们需要建立servlet映射表,利用到了反射和hashMap的具体实现。)
[4.7.3需要注意的是: 1.这个容器的path作为key值,将对象作为value值,(因为所有的servlet对象的父类都是HttpServlet,所以value直接定义为HttpServlet类型)](#4.7.3需要注意的是: 1.这个容器的path作为key值,将对象作为value值,(因为所有的servlet对象的父类都是HttpServlet,所以value直接定义为HttpServlet类型))
1、servlet的加载流程
1、Servlet加载流程:
Servlet 的实现是 "接口→抽象类→具体实现" 的逐层简化流程:
- Servlet 接口 :定义了 Servlet 的核心生命周期方法(
init()、service()、destroy()等),是 Servlet 规范的 "顶层契约"。 - GenericServlet 抽象类 :实现了 Servlet 接口中除
service()外的所有方法 (帮我们简化了配置获取、信息获取等通用逻辑),但service()仍需子类实现。 - HttpServlet 抽象类 :继承 GenericServlet,实现了
service()方法 ,并把它拆分成对应 HTTP 请求方式的方法(doGet()、doPost()、doPut()等)------ 这样我们不用自己判断请求类型,只需重写对应方法即可。 - 自定义类 :继承 HttpServlet,重写
doGet()/doPost()等方法,完成具体的业务逻辑。
| 层级 | 作用 & 核心点 |
|---|---|
| Servlet 接口 | 定义生命周期核心方法(init()/service()/destroy()等),是规范顶层契约 |
| GenericServlet | 实现 Servlet 接口的 "非 service 方法",简化配置 / 信息获取的通用逻辑 |
| HttpServlet | 继承 GenericServlet,实现service()并拆分出doGet()/doPost()适配 HTTP 请求 |
| 自定义类 | 继承 HttpServlet,重写doGet()/doPost()写业务逻辑 |
2、Servlet 的加载(生命周期)原理
Servlet 的加载 是由Web 容器(如 Tomcat) 管理的,核心流程是:
-
实例化(创建对象) Web 容器在第一次收到该 Servlet 的请求时(或容器启动时,若配置了
load-on-startup),通过反射创建 Servlet 对象。 -
初始化(
init()) 容器调用init(ServletConfig config)方法,完成 Servlet 的初始化(比如加载配置、初始化资源)。注:GenericServlet 已经帮我们实现了init()的通用逻辑,我们一般不用重写,除非有自定义初始化需求。 -
提供服务(
service()) 每次收到请求,容器都会调用service()方法:- 若直接实现 Servlet 接口:需要自己在
service()里处理请求 / 响应。 - 若继承 HttpServlet:
service()会自动根据 HTTP 请求方式(GET/POST 等),调用对应的doGet()/doPost()(这也是我们实际开发中重写这两个方法的原因)。
- 若直接实现 Servlet 接口:需要自己在
-
销毁(
destroy()) 当 Web 容器关闭或 Servlet 被移除时,调用destroy()方法,释放资源(比如关闭连接、清理缓存)。
3、为什么要这么设计?
这套结构是为了 **简化开发**:
- Servlet 接口定义规范,但直接实现太繁琐(要写所有方法)。
- GenericServlet 帮我们做了通用逻辑的 "兜底实现"。
- HttpServlet 进一步适配 HTTP 协议,把
service()拆分成 HTTP 请求对应的方法,让我们只需要关注具体请求的业务逻辑。

2、tomcat执行流程

上图为Tomcat 执行流程,将其拆成更细以下几个步骤:
步骤 1:请求到达服务器
用户通过浏览器发送http://ip:8080/xxx/my请求 → 先到达服务器的网卡(Tomcat 已在服务器上注册了 8080 端口,所以请求会被 Tomcat 接收)。
步骤 2:Tomcat 通过 Socket 建立连接
Tomcat 通过Socket(网络通信组件) 打开输入流(InputStream) ,接收请求数据;同时准备好输出流(OutputStream),用于后续返回响应。
步骤 3:解析 HTTP 请求数据
Tomcat 通过输入流读取请求内容,并解析请求的核心信息,包括:
- 来源 IP、端口
- URL 地址(比如这里的
/my) - 请求类型(比如这里的
get) - Cookie、请求参数等数据
步骤 4:Servlet 容器匹配对应的 Servlet 对象
Tomcat 内部有一个Servlet 容器(本质是 "地址→Servlet 对象的地址" 的映射表):
- 容器根据解析出的请求地址(比如
/my),找到对应的MyServlet对象(图中地址是 0x2); - 若请求地址是
/login,则会匹配loginServlet对象(地址 0x1)。
步骤 5:调用 Servlet 的业务方法处理请求
找到对应的MyServlet对象后,Tomcat 会根据请求类型(这里是get),调用 Servlet 中对应的方法(比如doGet()):
- 在
doGet()方法中,会执行具体的业务逻辑(比如处理请求参数、操作数据库等),最终生成响应数据(response)。
步骤 6:返回响应
Tomcat 通过输出流(OutputStream) ,把doGet()生成的响应数据,通过 Socket、网卡返回给浏览器,完成一次请求的处理。
简单总结:请求到网卡→Socket 接流→解析请求→容器匹配 Servlet→调用 do 方法处理→返回响应。

有三个是 Tomcat 等服务器常用的网络 IO 模型,核心是处理 "请求连接 + 数据读写" 的方式不同,我用通俗的方式解释:
1. BIO(Blocking IO,阻塞 IO)
- 本质:"一个请求对应一个线程",全程阻塞。
- 过程 :当客户端发起请求,Tomcat 会启动一个新线程 去处理这个请求:
- 线程会一直 "堵" 在
Socket的输入流 / 输出流上(比如等请求数据、等响应写完); - 直到这个请求完全处理完,线程才会被释放。
- 线程会一直 "堵" 在
- 特点:简单易理解,但线程资源消耗大(高并发时线程数爆炸)。
2. NIO(Non-Blocking IO,非阻塞 IO)
- 本质 :"一个线程管理多个请求",基于多路复用器(Selector) 实现。
- 过程 :Tomcat 用一个(或少量)线程通过
Selector监听所有连接的状态 :- 当某个连接的 "请求数据到了""可以写响应了",
Selector会通知线程处理这个连接; - 线程处理时不会阻塞(没数据就去处理其他连接)。
- 当某个连接的 "请求数据到了""可以写响应了",
- 特点:线程利用率高,能支撑更高并发(Tomcat 默认用 NIO)。
3. AIO(Asynchronous IO,异步 IO)
- 本质:"请求提交后不用管,等系统通知结果",完全异步。
- 过程:Tomcat 发起 "读 / 写请求" 后,直接去做其他事;等操作系统把数据准备好(或写完成),会主动通知 Tomcat 来处理结果。
- 特点:彻底解放线程,但实现复杂,适合连接数极多但数据交互少的场景(Tomcat 用得较少)。
对比总结
| 模型 | 核心逻辑 | 并发能力 | 复杂度 |
|---|---|---|---|
| BIO | 一请求一线程,全程阻塞 | 低 | 简单 |
| NIO | 线程 + Selector 管多连接 | 中高 | 中等 |
| AIO | 异步通知,线程不等待 | 高 | 复杂 |
3、手写tomcat思路流程

上图展示了 **手写简易 Tomcat" 的核心实现流程 **,本质是模拟 Tomcat 的 "请求处理 + Servlet 映射" 逻辑,分启动阶段 和请求处理阶段详细拆解:
3.1 启动阶段:初始化 Servlet 容器
手写 Tomcat 启动时,要先准备好 "Servlet 映射表",步骤如下:
-
扫描项目文件 遍历项目目录,找到所有标记了
@WebServlet注解的类,获取这些类的全路径名 (比如com.example.MyServlet)。 -
解析 Servlet 配置信息 读取
@WebServlet注解中的访问路径 (比如/my),这是客户端访问该 Servlet 的 URL 路径。 -
生成 Servlet 容器(本质是 HashMap) 创建一个
HashMap作为 "Servlet 映射表":- Key :Servlet 的访问路径(比如
/my); - Value :通过反射 创建的 Servlet 对象(用类的全路径名,调用
Class.forName(类名).newInstance()生成实例)。
- Key :Servlet 的访问路径(比如
3.2 请求处理阶段:接收请求→处理→响应
当客户端发送请求后,手写 Tomcat 会按以下流程处理:
-
接收请求(Socket 通信) 通过
Socket打开输入流 ,读取客户端发送的 HTTP 请求信息(比如请求行、请求头、请求体);同时打开输出流,用于后续返回响应。 -
封装请求对象(HttpServletRequest) 创建自定义的
HttpServletRequest类,把读取到的请求信息(路径、方法、参数等)全部封装到这个对象中,方便后续使用。 -
匹配请求目标 从
HttpServletRequest中获取请求路径,用这个路径作为 Key,去查之前创建的HashMap容器:- 如果匹配到 Servlet :从容器中取出对应的 Servlet 对象,再根据请求方法(GET/POST),调用 Servlet 的
doGet()/doPost()方法处理请求。 - 如果没匹配到 Servlet :判断请求路径是否对应静态资源 (比如.html、.css、.js):
- 是静态资源:读取资源文件,准备返回;
- 不是:返回 404 错误。
- 如果匹配到 Servlet :从容器中取出对应的 Servlet 对象,再根据请求方法(GET/POST),调用 Servlet 的
-
封装响应对象(HttpServletResponse) 创建自定义的
HttpServletResponse类,把处理结果(Servlet 的业务数据 / 静态资源内容 / 404 信息)封装到这个对象中。 -
返回响应 通过
Socket的输出流,把HttpServletResponse中的数据以 HTTP 格式写回客户端,完成请求处理。
3.3 核心本质
手写 Tomcat 的核心是 **"反射 + HashMap 映射 + Socket 通信"**:用反射创建 Servlet 对象,用 HashMap 管理 "路径→Servlet" 的映射,用 Socket 完成网络请求的收发,模拟了 Tomcat 的核心工作逻辑。
4、具体手写tomcat
4.1 首先在idea中创建maven项目
4.2 我们在使用tomcat的时候,我们会发现我们的servlet会放在webapps文件夹里面,所以我们也创建一个webapps。
4.3 在webapps里面创建MyfirstServlet和MysecondServlet。
4.4 接着我们回看之前servlet的加载流程,以MyfirstServlet举例,MyfirstServlet继承HttpServlet,HttpServlet里面是继承GenericServlet并重写service()方法,而在GenericServlet里面是实现了Servlet接口里面的除service()方法以外的所有方法。
4.5 在这里本应该是重写doget、dopost等方法,考虑到实际的业务,这里暂时跳过。
4.6 这是我们考虑到了第三步@WebServlet注解。
关于注解我们需要了解最重要的三点: 注解作用范围、注解的生命周期以及注解名字的规定
java
@Retention(RetentionPolicy.RUNTIME) //自定义注解的生命周期
@Target(ElementType.TYPE) //自定义注解的作用范围,就是可以放在哪里
public @interface WebServlet {
String value(); //默认的 类型 名称()
}
其中String 默认为value(),但是如果变为其他的命名:需要在使用时进行具体赋值。
例如:
java
@Retention(RetentionPolicy.RUNTIME) //自定义注解的生命周期
@Target(ElementType.TYPE) //自定义注解的作用范围,就是可以放在哪里
public @interface WebServlet {
String val(); //默认的 类型 名称()
}
java
@WebServlet(val = "/myfirst")
public class MyfirstServlet extends HttpServlet {
}
4.7接下来我们需要建立servlet映射表,利用到了反射和hashMap的具体实现。
4.7.1反射获取全类名的三种方式:
1.通过 Class.forName() 获取(动态加载类)
2.没有对象实例的场景,直接通过 类名.class 获取 Class 对象
3.通过对象的 getClass() 方法获取 Class 对象
要一一扫描项目文件 遍历项目目录,找到所有标记了@WebServlet注解的类,获取这些类的全路径名 (比如com.example.MyServlet)我们可以定义一个工具类
4.7.2关于工具类:
1.都是静态方法,要对外访问的用public,不让外界访问的私有化private。
2.有main方法,不创建对象
工具包:
java
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* 扫描指定包,获取该包下所有的类的全路径信息
*/
public class SearchClassUtil {
public static List<String> classPaths = new ArrayList<String>();
public static List<String> searchClass(String path){
//需要扫描的包名
String basePack = path;
//将获取到的包名转换为路径
String classPath = SearchClassUtil.class.getResource("/").getPath();
basePack = basePack.replace(".", File.separator);
String searchPath = classPath + basePack;
doPath(new File(searchPath),classPath);
//这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
return classPaths;
}
/**
* 该方法会得到所有的类,将类的绝对路径写入到classPaths中
* @param file
*/
private static void doPath(File file,String classpath) {
if (file.isDirectory()) {//文件夹
//文件夹我们就递归
File[] files = file.listFiles();
for (File f1 : files) {
doPath(f1,classpath);
}
} else {//标准文件
//标准文件我们就判断是否是class文件
if (file.getName().endsWith(".class")) {
String path = file.getPath().replace(classpath.replace("/","\\").
replaceFirst("\\\\",""),"").replace("\\",".").
replace(".class","");
//如果是class文件我们就放入我们的集合中。
classPaths.add(path);
}
}
}
public static void main(String[] args) {
List<String> classes = SearchClassUtil.searchClass("com.包名.webapps.myweb");
for (String s: classes) {
System.out.println(s);
}
}
}
创建servlet容器利用hashmap
ServletConfigMapping:生成servlet容器
java
import com.qcby.lib.HttpServlet;
import com.qcby.lib.WebServlet;
import com.qcby.utils.SearchClassUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* servlet容器配置类
*/
public class ServletConfigMapping {
/**
*
*/
public static Map<String, HttpServlet> classMapping = new HashMap<>();//servlet容器
//特点:在main方法执行之前加载,以修饰main方法
static {
// 1. 扫描指定包下的所有类
List<String> paths = SearchClassUtil.searchClass("com.qcby.webapps.myweb");
try {
// 2. 遍历类,解析@WebServlet注解
for (String path : paths) {
//获取类的注解信息,并将path作为key值,将对象作为value值 存放在classMapping当中
Class clazz = Class.forName(path);//获取类对象
//获取类的注解信息
// 通过反射机制,从目标类(clazz)中提取它上面标注的@WebServlet注解对象,后续可通过该对象获取注解的属性(比如value()获取 Servlet路径、name()获取 Servlet名称等)。
WebServlet webServlet = (WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
//System.out.println(webServlet.value());
//这里获取类的无参构造方法 ---创建新的对象并强转向上转型为HttpServlet
HttpServlet servlet = (HttpServlet)clazz.getDeclaredConstructor().newInstance();
classMapping.put(webServlet.value(), servlet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.7.3需要注意的是:
1.这个容器的path作为key值,将对象作为value值,(因为所有的servlet对象的父类都是HttpServlet,所以value直接定义为HttpServlet类型)
2.扫描包的类用的是一个SearchClassUtil工具类进行实现的
2.1 首先 进行遍历获取类信息 Class clazz = Class.forName(path)
2.2通过反射机制,从目标类(clazz)中提取它上面标注的@WebServlet注解对象
2.3这里获取类的无参构造方法 ---创建新的对象并强转向上转型为
HttpServlet HttpServlet servlet = (HttpServlet)clazz.getDeclaredConstructor().newInstance();
2.4 存入servlet容器 classMapping.put(webServlet.value(), servlet);
4.8 新MyTomcat文件,在MyTomcat启动器里面获取请求信息,我们会发现在浏览器里面访问一个资源的时候,idea会返回请求信息,因为我们定义了一个端口号7788
MyTom启动类:
java
import com.qcby.config.ServletConfigMapping;
import com.qcby.lib.HttpServlet;
import com.qcby.lib.HttpServletRequest;
import com.qcby.lib.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTomcat {
//创建HttpServletRequest的对象用来保存请求信息的
private static HttpServletRequest httpServletRequest = new HttpServletRequest();
//创建HttpServletResponse的对象用来返回给客户端前端
private static HttpServletResponse httpServletResponse = new HttpServletResponse();
private static final Integer PORT = 7788; //监听的端口号
public static void start() throws Exception {
ServerSocket serverSocket = new ServerSocket(PORT);
Socket socket = serverSocket.accept();//阻塞监听
InputStream stream = socket.getInputStream();//输入流
httpServletResponse.setOutputStream(socket.getOutputStream());//输出流
hanlder(stream);
}
/**
* 处理客户端的请求数据,并进行保存
* @param stream
* @throws IOException
*/
public static void hanlder(InputStream stream) throws Exception {
int count = 0;
while(count == 0){
count = stream.available();
}
byte[] bytes = new byte[count];
int read = stream.read(bytes);
String request = new String(bytes, 0, read);
//System.out.println(request);
if(request.equals("")){
System.out.println("请求为null");
}else{
String firstLine = request.split("\n")[0];
String method = firstLine.split("\\s")[0];//请求方式
String path = firstLine.split("\\s")[1];//请求路径
System.out.println("请求方式:"+method+' '+"请求路径:"+path);
httpServletRequest.setMethod(method);//传入
httpServletRequest.setPath(path);//传入
if (path.equals("")){
System.out.println("请求为空");
}else if (ServletConfigMapping.classMapping.get(path) != null){
//动态
//虽然是用的HttpServlet对象进行接收,但是实际上我们返回的是MyFirstServlet/MySecondServlet
//HttpServlet 实际上是向上转型的产物
HttpServlet servlet = ServletConfigMapping.classMapping.get(path);
servlet.service(httpServletRequest,httpServletResponse);
}else{
//静态
httpServletResponse.returnStatic(path);
}
}
}
/**
* tomcat启动类
*/
public static void main(String[] args) throws Exception {
start();
}
}
4.9 在hettpservletrequest中实现servletrequest的get和set方法,用来保存4.8 获取请求信息,比如method请求方法,path请求路径。
ServletRequest接口:
java
public interface ServletRequest {
//定义get和set
String getMethod();
String getPath();
String setMethod(String method);
String setPath(String path);
}
ServletResponse接口:
java
public interface ServletResponse {
void append(String responseHeader200) throws Exception;
}
HttpServletRequest类:
java
public class HttpServletRequest implements ServletRequest{
private String method;
private String path;
@Override
public String getMethod() {
return method;
}
@Override
public String getPath() {
return path;
}
@Override
public String setMethod(String method) {
return this.method = method;
}
@Override
public String setPath(String path) {
return this.path = path;
}
}
HttpServletResponse类:
java
package com.qcby.lib;
import com.qcby.utils.FileUtil;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
public class HttpServletResponse implements ServletResponse{
//获取输出流
private OutputStream outputStream;
public OutputStream getOutputStream() {
return outputStream;
}
public void setOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void append(String context) throws IOException {
outputStream.write(context.getBytes());
}
/**
* 返回静态资源
* @param path 静态资源文件的相对路径
* @throws Exception
*/
public void returnStatic(String path) throws Exception{
String resouce = FileUtil.getResoucePath(path);//获取静态资源的真实路径
File file = new File(resouce); //静态文件
if (file.exists()){
//静态文件存在
FileUtil.writeFile(file,outputStream);
}else{
System.out.println("404静态资源文本不存在......");
}
}
}
在输入流返回信息中需要进行信息的截取

4.10 此时request里面已经有数据了,此时回看之前跳过的4.5编写dopost和get方法的时候,编写请求里面具体的post和get方法。
Servlet接口:
java
public interface Servlet {
void init() ;
void service(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception;
void destroy();
}
GenericServlet抽象类:实现了Servlet接口
java
public abstract class GenericServlet implements Servlet{
@Override
public void init() {
}
@Override
public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception;
@Override
public void destroy() {
}
}
HttpServlet抽象类:继承了GenericServlet为父类
java
package com.qcby.lib;
public abstract class HttpServlet extends GenericServlet {
public abstract void doGet(ServletRequest request, ServletResponse response) throws Exception;
public abstract void doPost(ServletRequest request, ServletResponse response);
@Override
public void service(ServletRequest request, ServletResponse response) throws Exception {
//此时已经有servletRequest的信息
//对应的请求
if (request.getMethod().equals("GET")) {
doGet(request, response);
} else if (request.getMethod().equals("Post")) {
doPost(request, response);
}
}
}
例如:自定义类MyfirstServlet
java
package com.qcby.webapps.myweb;
import com.qcby.lib.HttpServlet;
import com.qcby.lib.ServletRequest;
import com.qcby.lib.ServletResponse;
import com.qcby.lib.WebServlet;
import com.qcby.utils.ResponseUtil;
/**
* 我们在创建第一个servlet程序的时候注意了三点
* 1.继承HttpServlet
* 2.重写doget或者dopost方法
* 3.引入@WebServlet注解 --- 详细注解的注意事项
*/
@WebServlet("/myfirst")
public class MyfirstServlet extends HttpServlet {
@Override
public void doGet(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("MyFirstServlet doGet 这里的地址");
response.append(ResponseUtil.getResponseHeader200("<h1>这里是第一个Servlet</h1>"));
}
@Override
public void doPost(ServletRequest request, ServletResponse response) {
}
}
MysecondServlet:
java
package com.qcby.webapps.myweb;
import com.qcby.lib.HttpServlet;
import com.qcby.lib.ServletRequest;
import com.qcby.lib.ServletResponse;
import com.qcby.lib.WebServlet;
@WebServlet("/mysecond")
public class MysecondServlet extends HttpServlet {
@Override
public void doGet(ServletRequest request, ServletResponse response) {
}
@Override
public void doPost(ServletRequest request, ServletResponse response) {
}
}
4.11 将请求信息与map集合的servlet进行匹配进行执行,
MyTomcat中:
java
if (path.equals("")){
System.out.println("请求为空");
}else if (ServletConfigMapping.classMapping.get(path) != null){
//动态
//虽然是用的HttpServlet对象进行接收,但是实际上我们返回的是MyFirstServlet/MySecondServlet
//HttpServlet 实际上是向上转型的产物
HttpServlet servlet = ServletConfigMapping.classMapping.get(path);
servlet.service(httpServletRequest,httpServletResponse);
}else{
//静态
httpServletResponse.returnStatic(path);
}
4.12 返回
静态资源:引入一个文件工具类
java
package com.qcby.utils;
import java.io.*;
/**
* 该类的主要作用是进行读取文件
*/
public class FileUtil {
public static boolean witeFile(InputStream inputStream, OutputStream outputStream){
boolean success = false ;
BufferedInputStream bufferedInputStream ;
BufferedOutputStream bufferedOutputStream;
try {
bufferedInputStream = new BufferedInputStream(inputStream);
bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write(ResponseUtil.responseHeader200.getBytes());
int count = 0;
while (count == 0){
count = inputStream.available();
}
int fileSize = inputStream.available();
long written = 0;
int beteSize = 1024;
byte[] bytes = new byte[beteSize];
while (written < fileSize){
if(written + beteSize > fileSize){
beteSize = (int)(fileSize - written);
bytes = new byte[beteSize];
}
bufferedInputStream.read(bytes);
bufferedOutputStream.write(bytes);
bufferedOutputStream.flush();
written += beteSize;
}
success = true;
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
public static boolean writeFile(File file,OutputStream outputStream) throws Exception{
return witeFile(new FileInputStream(file),outputStream);
}
public static String getResoucePath(String path){
String resource = FileUtil.class.getResource("/").getPath();
return resource + "\\" + path;
}
}
在tomcat启动类中
java
else{
//静态
httpServletResponse.returnStatic(path);
}
HttpServletResponse响应类
java
/**
* 返回静态资源
* @param path 静态资源文件的相对路径
* @throws Exception
*/
public void returnStatic(String path) throws Exception{
String resouce = FileUtil.getResoucePath(path);//获取静态资源的真实路径
File file = new File(resouce); //静态文件
if (file.exists()){
//静态文件存在
FileUtil.writeFile(file,outputStream);
}else{
System.out.println("404静态资源文本不存在......");
}
}
