本步骤将实现基于 web.xml
配置文件的 Servlet 路由映射和初始化参数支持,使得 MiniTomcat 能根据配置自动调用相应的 Servlet 处理请求。
5.1 功能目标
配置文件管理路由和初始化参数:通过 web.xml
文件管理 Servlet 映射和初始化参数。
URL 路径映射:根据配置文件中的路径映射,将请求 URL 映射到对应的 Servlet。
支持 ServletConfigImpl
获取初始化参数:提供 ServletConfigImpl
类,支持从配置中读取初始化参数。
5.2 代码结构
以下是更新后的 MiniTomcat 代码结构,新增了 ServletConfigImpl
、ServletContextImpl
、ServletLoader
和WebXmlServletContainer
等类和 web.xml
配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| MiniTomcat ├─ src │ ├─ main │ │ ├─ java │ │ │ ├─ com.daicy.minitomcat │ │ │ │ ├─ servlet │ │ │ │ │ ├─ CustomServletOutputStream.java // 自定义的 Servlet 输出流类 │ │ │ │ │ ├─ HttpServletRequestImpl.java // HTTP 请求的实现类 │ │ │ │ │ ├─ HttpServletResponseImpl.java // HTTP 响应的实现类 │ │ │ │ │ ├─ ServletConfigImpl.java // Servlet 配置的实现类 │ │ │ │ │ ├─ ServletContextImpl.java // Servlet 上下文的实现类 │ │ │ │ ├─ HelloServlet.java // Servlet 示例类 │ │ │ │ ├─ HttpConnector.java // 连接器类 │ │ │ │ ├─ HttpProcessor.java // 请求处理器 │ │ │ │ ├─ HttpServer.java // 主服务器类 │ │ │ │ ├─ ServletLoader.java // Servlet 加载器 │ │ │ │ ├─ ServletProcessor.java // Servlet 处理器 │ │ │ │ ├─ StaticResourceProcessor.java// 静态资源处理器 │ │ │ │ ├─ WebXmlServletContainer.java // Servlet 容器相关类 │ │ ├─ resources │ │ │ ├─ webroot │ │ │ │ ├─ index.html │ │ │ ├─ web.xml │ ├─ test ├─ pom.xml
|
5.3 代码实现
5.3.1 创建 ServletConfigImpl
类
ServletConfigImpl
用于存储 web.xml
中的初始化参数,提供 getInitParameter()
方法获取这些参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.daicy.minitomcat.servlet;
public class ServletConfigImpl implements ServletConfig {
private String servletName; private ServletContext servletContext; private Map<String, String> initParameters;
public ServletConfigImpl(String servletName, ServletContext servletContext, Map<String, String> initParameters) { this.servletName = servletName; this.servletContext = servletContext; this.initParameters = initParameters != null ? initParameters : new HashMap<>(); }
@Override public String getInitParameter(String name) { return initParameters.get(name); } ...... }
|
5.3.2 添加 web.xml
文件
在 resources
目录下创建 web.xml
文件,用于配置 Servlet 映射和初始化参数。配置 /hello
路径映射到 HelloServlet
类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <web-app> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.daicy.minitomcat.HelloServlet</servlet-class> <init-param> <param-name>greeting</param-name> <param-value>Hello from web.xml!</param-value> </init-param> </servlet>
<servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
|
5.3.3 解析 web.xml
配置文件
创建一个 WebXmlServletContainer
类,用于解析 web.xml
文件,并将 Servlet 路径映射到对应的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| package com.daicy.minitomcat;
import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory;
import com.daicy.minitomcat.servlet.ServletConfigImpl; import org.w3c.dom.*;
import java.util.HashMap; import java.util.Map; import javax.servlet.ServletConfig;
public class WebXmlServletContainer {
private Map<String, ServletConfig> servletConfigMap = new HashMap<>();
private Map<String, Servlet> servletHashMap = new HashMap<>();
private ServletContext servletContext;
public void parse(String xmlPath, ServletContext servletContext) { try { this.servletContext = servletContext;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(getClass().getResourceAsStream(xmlPath));
NodeList servletNodes = doc.getElementsByTagName("servlet"); for (int i = 0; i < servletNodes.getLength(); i++) { Element servletElement = (Element) servletNodes.item(i); String servletName = servletElement.getElementsByTagName("servlet-name").item(0).getTextContent(); String servletClass = servletElement.getElementsByTagName("servlet-class").item(0).getTextContent();
Map<String, String> initParamsMap = new HashMap<>(); NodeList initParams = servletElement.getElementsByTagName("init-param"); for (int j = 0; j < initParams.getLength(); j++) { Element param = (Element) initParams.item(j); String paramName = param.getElementsByTagName("param-name").item(0).getTextContent(); String paramValue = param.getElementsByTagName("param-value").item(0).getTextContent(); initParamsMap.put(paramName, paramValue); } ServletConfig servletConfig = new ServletConfigImpl(servletName, servletContext, initParamsMap); servletConfigMap.put(servletClass, servletConfig); servletContext.setAttribute(servletName, servletClass); }
NodeList mappingNodes = doc.getElementsByTagName("servlet-mapping"); for (int i = 0; i < mappingNodes.getLength(); i++) { Element mappingElement = (Element) mappingNodes.item(i); String servletName = mappingElement.getElementsByTagName("servlet-name").item(0).getTextContent(); String urlPattern = mappingElement.getElementsByTagName("url-pattern").item(0).getTextContent(); servletContext.setAttribute(urlPattern, servletName); } } catch (Exception e) { e.printStackTrace(); } }
public ServletConfig getServletConfig(String urlPattern) { String servletClass = getServletClass(getServletName(urlPattern)); return servletConfigMap.get(servletClass); }
public String getServletName(String urlPattern) { return (String) servletContext.getAttribute(urlPattern); }
public String getServletClass(String servletName) { return (String) servletContext.getAttribute(servletName); }
public Servlet getServlet(String servletName) { return servletHashMap.get(servletName); }
public void setServlet(String servletName,Servlet servlet) { servletHashMap.put(servletName,servlet); }
public Map<String, Servlet> getServletHashMap() { return servletHashMap; } }
|
5.3.4 修改 ServletProcessor
类
在 ServletProcessor
中使用 WebXmlServletContainer
获取 Servlet 映射信息和初始化参数,并调用对应的 Servlet 处理请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package com.daicy.minitomcat;
import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;
import static com.daicy.minitomcat.HttpProcessor.send404Response;
public class ServletProcessor {
public void process(HttpServletRequest request, HttpServletResponse response) { String uri = request.getRequestURI(); try { PrintWriter writer = response.getWriter(); WebXmlServletContainer parser = HttpServer.parser; String servletName = parser.getServletName(uri); if (null != servletName) { writeResponseHeaders(writer, 200, "OK"); Servlet servlet = parser.getServlet(servletName); if (null == servlet){ ServletConfig servletConfig = parser.getServletConfig(uri); servlet = ServletLoader.loadServlet(servletConfig); if (null == servlet){ return; } parser.setServlet(servletName, servlet); } servlet.service(request, response); } else { send404Response(writer); } } catch (IOException e) { e.printStackTrace(); } catch (ServletException e) { throw new RuntimeException(e); } }
private void writeResponseHeaders(PrintWriter writer, int statusCode, String statusMessage) { writer.println("HTTP/1.1 " + statusCode + " " + statusMessage); writer.println("Content-Type: text/html; charset=UTF-8"); writer.println(); }
}
|
5.3.5 修改 HelloServlet
支持初始化参数
更新 HelloServlet
,使用 ServletConfig
获取 web.xml
中的初始化参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package server.servlet;
import server.HttpServletRequest; import server.HttpServletResponse; import server.ServletConfig;
import java.io.IOException;
public class HelloServlet { private ServletConfig config;
public void init(ServletConfig config) { this.config = config; }
public void service(HttpServletRequest request, HttpServletResponse response) { try { String greeting = config.getInitParameter("greeting"); response.getWriter().println("<html><body><h1>" + greeting + "</h1></body></html>"); } catch (IOException e) { e.printStackTrace(); } }
public void destroy() { } }
|
5.4 测试
启动服务器并访问 http://localhost:8080/hello
,将返回 web.xml
中设置的 greeting
参数:“Hello from web.xml!”
5.5 学习收获
通过本步骤的实现,我们学到了:
XML 配置文件解析:掌握了解析 XML 文件的方法,并将 XML 数据映射为对象。
Servlet URL 路径映射:基于配置文件实现了灵活的 URL 路径到 Servlet 的映射机制。
Servlet 初始化参数的使用:学习了如何使用 ServletConfig
获取 web.xml
中的初始化参数。
这为后续的会话管理、过滤器支持等功能的实现打下了基础。
项目源代码地址:
https://github.com/daichangya/MiniTomcat/tree/chapter5/mini-tomcat