DIY-Tomcat手写一个简易Servlet容器
DIY Tomcat:手写一个简易Servlet容器
在Java Web开发领域,Tomcat堪称经典,它作为Servlet容器,承载着无数Web应用的运行。今天,我将带大家一同探索如何手写一个简易的Tomcat,深入理解其底层原理。
一、背景知识
在开始之前,我们需要对几个关键概念有所了解:
- Servlet :是一种运行在服务器端的 Java 接口,用于响应客户端请求并生成动态内容。
- Servlet 容器 :负责管理 Servlet 的生命周期,包括加载、实例化、调用和销毁等操作。
- Socket 编程 :用于实现网络通信,Tomcat 通过监听 Socket 接收客户端请求。
二、核心代码解析
1 MyTomcat.java
这是整个简易 Tomcat 的核心入口。我们创建了一个 ServerSocket 对象,监听 8080 端口,等待客户端的连接。 ServerSocket serverSocket = new ServerSocket(8080); while (true) { Socket socket = serverSocket.accept(); // 处理客户端请求的逻辑 } 当有客户端连接时,通过 Socket 获取输入流,读取请求信息,并解析出请求方法和路径。 InputStream inputStream= socket.getInputStream(); HttpServletResponse response=new HttpServletResponse(socket); HttpServletRequest request=new HttpServletRequest(); int count=0; while(count==0){ count=inputStream.available(); } byte[] bytes=new byte[count]; inputStream.read(bytes); String context=new String(bytes); // 解析请求方法和路径 String Context = new String(bytes); String firstLine = Context.split("\n")[0]; String method = firstLine.split("\s")[0]; String path = firstLine.split("\s")[1]; request.setPath(path); request.setMethod(method); 根据解析出的路径,在 Servlet 容器中查找对应的 Servlet 对象,并调用其 service 方法。 if(ServletConfigMapping.servletMap.get(request.getPath())!=null) { System.out.println("——————"); HttpServlet ClassServlet=ServletConfigMapping.servletMap.get(request.getPath()); ClassServlet.service(request, response); }
2 ServletConfigMapping.java
这个类负责初始化 Servlet 容器,扫描指定包下的所有类,获取带有 @WebServlet 注解的类,并将其路径和对象存入 HashMap 中。 public static void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException { List classesPath= SearchClassUtil.searchClass(); for(String path:classesPath){ getMessage(path); } } public static void getMessage(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class clazz=Class.forName(classPath); WebServlet webServlet=(WebServlet) clazz.getDeclaredAnnotation(WebServlet.class); HttpServlet servlet=(HttpServlet) clazz.newInstance(); servletMap.put(webServlet.urlMapping(),servlet); }
3 LoginServlet.java 和 ShowServlet.java
这两个类是具体的 Servlet 实现,继承自 HttpServlet,并重写 doGet 和 doPost 方法,用于处理不同的 HTTP 请求。 @WebServlet(urlMapping = “/login”) public class LoginServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) { // 处理 GET 请求的逻辑 } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) { // 处理 POST 请求的逻辑 } }
4 SearchClassUtil.java
该工具类用于扫描指定包下的所有类文件,获取其全类名,为 Servlet 容器的初始化提供类路径信息。 public static List searchClass() { String basePack = “com.qcby.webapps”; String classPath = SearchClassUtil.class.getResource("/").getPath(); try { classPath = URLDecoder.decode(classPath, StandardCharsets.UTF_8.name()); } catch (Exception e) { e.printStackTrace(); } basePack = basePack.replace(".", File.separator); String searchPath = classPath + basePack; doPath(new File(searchPath), classPath); return classPaths; }
5 WebServlet.java
这是一个自定义注解,用于标注 Servlet 类,并指定其映射的 URL 路径。 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface WebServlet { String urlMapping() default “”; }
6 GenericServlet.java、HttpServlet.java 和 Servlet.java
这些类和接口构成了 Servlet 的基本体系结构。Servlet 接口定义了 Servlet 的生命周期方法,GenericServlet 提供了通用的实现,HttpServlet 则针对 HTTP 请求进行了特殊处理,将请求分发到 doGet 或 doPost 方法。 public interface Servlet { void init(); void destroy(); void service(HttpServletRequest request, HttpServletResponse response); } public abstract class GenericServlet implements Servlet{ @Override public void init() {} @Override public void destroy() {} } public abstract class HttpServlet extends GenericServlet { public void service(HttpServletRequest request, HttpServletResponse response){ if(request.getMethod().equals(“GET”)) { doGet(request,response); } else if(request.getMethod().equals(“POST”)) { doPost(request,response); } } public abstract void doGet(HttpServletRequest request,HttpServletResponse response); public abstract void doPost(HttpServletRequest request,HttpServletResponse response); }
7 HttpServletRequest.java 和 HttpServletResponse.java
这两个类用于封装请求和响应信息,方便在 Servlet 中进行操作。 public class HttpServletRequest { private String method; private String path; // getter 和 setter 方法 } public class HttpServletResponse { }
三、运行效果展示
将上述代码整合后,启动 MyTomcat,它便会开始监听 8080 端口。当我们在浏览器中访问对应的路径,如
http://localhost:8080/login
或 http://localhost:8080/show
时,Tomcat
会根据请求路径找到对应的 Servlet,并调用其 doGet 或 doPost 方法来处理请求,最终返回相应的响应。
四、总结与展望
通过手写这个简易的 Tomcat,我们深入理解了 Servlet 容器的基本工作原理,包括 Socket 编程、请求解析、Servlet 的加载和调用等关键环节。虽然这个实现功能简单,但它为我们进一步学习和研究 Tomcat 的源码提供了宝贵的实践经验。 在未来的学习中,我们可以继续完善这个简易 Tomcat,添加更多的功能,如支持静态资源的访问、会话管理、过滤器和监听器等,逐步使其功能更加丰富和完善,向真正的 Tomcat 靠拢。 希望这次的分享能为大家打开 Java Web 开发底层世界的大门,让我们一起在技术的海洋中不断探索和前行!