手写tomcat:基本功能实现(3)
TomcatRoute类
TomcatRoute类是Servlet容器,是Tomcat中最核心的部分,其本身是一个HashMap,其功能为:将路径和对象写入Servlet容器中。
package com.qcby.config;import com.qcby.Util.SearchClassUtil;
import com.qcby.servlet.Httpservlet;
import com.qcby.zj.YbyServlet;import java.util.HashMap;
import java.util.List;import java.util.List;
import java.util.Map;public class TomcatRoute {public static HashMap<String, Httpservlet> Route = new HashMap<>();static {List<String> classesPath = SearchClassUtil.searchClass();for (String path : classesPath) {try {//加载类Class clazz = Class.forName(path);//获取注解YbyServlet webServlet = (YbyServlet) clazz.getDeclaredAnnotation(YbyServlet.class);
// 对象Object servlet = clazz.getDeclaredConstructor().newInstance();Route.put(webServlet.url(), (Httpservlet) servlet);
// Httpservlet servlet = (Httpservlet) clazz.newInstance();
// servletMap.put(webServlet.url(),servlet);System.out.println(Route);} catch (Exception e) {e.printStackTrace();}}}}
静态代码块
1. 确保初始化时机
静态代码块在类加载时自动执行,且仅执行一次。这确保了路由表在程序启动时就被初始化,后续无需手动调用初始化方法。
对比其他初始化方式:
- 构造函数:每次创建对象时都会执行,而路由表只需初始化一次。
- 普通静态方法:需要手动调用,可能被遗忘或重复调用。
- 静态代码块:自动触发,保证全局唯一性。
2. 全局唯一性
静态代码块属于类,而非某个实例。无论创建多少个TomcatRoute
对象,路由表只会初始化一次。
功能
- 自动扫描:通过
SearchClassUtil.searchClass()
扫描项目中的所有类。 - 注解识别:使用
@YbyServlet
注解标记需要注册的 Servlet。 - 路由映射:将注解中的
url()
值作为路径,对应的 Servlet 实例作为值,存入静态的Route
映射表。 - 类加载时初始化:利用静态代码块在类加载时执行初始化逻辑,确保路由表在程序启动时就已准备好。
public static HashMap<String, Httpservlet> Route = new HashMap<>();
1. 多态性的应用
Httpservlet
是所有具体 Servlet 的抽象父类(或接口),通过父类类型引用子类实例,可以实现统一调用。
2. 解耦路由逻辑与具体实现
路由系统只需关注请求路径与处理逻辑的映射关系,而无需关心具体 Servlet 的实现细节。
3. 统一接口定义
Httpservlet
通常定义了处理请求的标准方法(如service()
、doGet()
、doPost()
),所有子类必须实现这些方法。
4. 符合 Servlet 规范
在标准 Java Web 开发中,所有 Servlet 都必须实现javax.servlet.Servlet
接口(或继承HttpServlet
)。这种设计模仿了标准 Servlet 容器的工作原理。
MyTomcat
package com.qcby;
//tomcat主启动类
import com.qcby.config.TomcatRoute;
import com.qcby.servlet.Httpservlet;
import com.qcby.Request.HttpServletRequest;
import com.qcby.Response.HttpServletResponse;import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;public class Mytomcat {static HashMap<String,Httpservlet> routes= TomcatRoute.Route;static HttpServletRequest request=new HttpServletRequest();static HttpServletResponse response=new HttpServletResponse();public static void dispatch(){Httpservlet servlet=routes.get(request.getPath());if(servlet!=null){servlet.service(request,response);}}public static void main(String[] args) {try {System.out.append("服务器启动......");
// 1.定义ServerSocket对象进行服务器的端口注册ServerSocket serverSocket = new ServerSocket(8080);while (true) {
// 2.监听客户端的socket链接程序Socket socket = serverSocket.accept();//阻塞监听
// 3.从socket对象当中获得一个字节流对象InputStream iStream = socket.getInputStream();// 打印输出int len = 0;int ReviceLen = 0;
// 计算机网络数据是以8bit为一个单元进行发送,我们接收到发送方发生的byte数据
// 将其转化为utf-8格式输出byte[] recvBuf = new byte[1024];while ((ReviceLen = iStream.read(recvBuf)) != -1) {String count=new String(recvBuf,0,ReviceLen,"UTF-8");String method=count.split(" ")[0];String url=count.split(" ")[1];request.setMethod(method);request.setPath(url);}dispatch();}} catch(Exception e){// TODO: handle exception}}}
1. 整体架构
这个简易服务器包含三个核心组件:
- Mytomcat:主服务器类,负责接收请求和分发。
- TomcatRoute:路由配置类,通过静态代码块扫描并注册所有 Servlet。
- Httpservlet:抽象 Servlet 基类,定义请求处理接口。
2. 核心成员变量
static HashMap<String,Httpservlet> routes = TomcatRoute.Route;
static HttpServletRequest request = new HttpServletRequest();
static HttpServletResponse response = new HttpServletResponse();
routes
:从TomcatRoute
获取的路由表,存储 URL → Servlet 的映射关系。request
和response
:静态全局对象,用于存储当前请求和响应信息。
3. 请求分发逻辑
public static void dispatch() {Httpservlet servlet = routes.get(request.getPath());if (servlet != null) {servlet.service(request, response);}
}
- 根据
request.getPath()
从路由表中查找对应的 Servlet。 - 调用 Servlet 的
service()
方法处理请求(多态调用)。
4. 主服务器逻辑
public static void main(String[] args) {try {System.out.println("服务器启动......");ServerSocket serverSocket = new ServerSocket(8080);while (true) {// 1. 监听客户端连接(阻塞)Socket socket = serverSocket.accept();InputStream iStream = socket.getInputStream();// 2. 解析HTTP请求byte[] recvBuf = new byte[1024];int ReviceLen = iStream.read(recvBuf);String count = new String(recvBuf, 0, ReviceLen, "UTF-8");String method = count.split(" ")[0];String url = count.split(" ")[1];// 3. 设置请求信息request.setMethod(method);request.setPath(url);// 4. 分发请求dispatch();}} catch (Exception e) {e.printStackTrace();}
}
关键步骤:
- 创建服务器套接字:监听 8080 端口。
- 接收客户端连接:通过
serverSocket.accept()
阻塞等待请求。 - 解析 HTTP 请求:
- 读取请求头的第一行(格式:
GET /path HTTP/1.1
)。 - 提取 HTTP 方法(如
GET
)和请求路径(如/login
)。
- 读取请求头的第一行(格式:
- 设置请求对象:将解析结果存入静态
request
对象。 - 分发请求:调用
dispatch()
方法查找并执行对应的 Servlet。