资讯详情

11手写服务器HTTP Server

📅 2026/11/6 5:15:54 | 华诺云谱 👁 阅读
11手写服务器HTTP Server
反射下面这段代码演示了反射的两个核心步骤先获取Class对象再通过它动态创建实例。package com.server; /** 1.获取Class对象 2.动态创建对象 */ public class ReflectTest { public static void main(String[] args) throws Exception { // 三种获取Class对象的方式 // 方式1对象.getClass() Student stu new Student(); Class c1 stu.getClass(); // 方式2类.class c1 Student.class; // 方式3Class.forName(全类名) Class c2 Class.forName(com.server.Student); // 通过public无参构造器创建实例 Student student (Student) c2.getConstructor().newInstance(); System.out.println(student); } } class Student { public String name; private int age; public Student() { } }XML 解析XML 解析的核心目标是把一段 XML 文本转换成程序可以访问的对象结构。Java 中常见的解析方式有四种DOM、SAX、JDOM和DOM4J。其中 SAX 是唯一一种完全基于事件流、不需要把整份文档加载进内存的解析方式适合处理大文件。SAX 的核心思想SAXSimple API for XML是一种推模型Push的流式解析器。解析器从文档开头逐字符扫描遇到开始标签、文本、结束标签等结构时主动回调注册好的处理器方法。整个过程只保留当前节点信息内存占用与文档大小基本无关。顺序读取只能从头到尾读一遍不能回退也不能随机访问某个节点。事件驱动解析器负责“推”开发者只需在回调里写业务逻辑。低内存不构建 DOM 树适合几百 MB 甚至更大的 XML 文件。只读SAX 只能解析不能修改或回写 XML。SAX 与 DOM 的对比对比项SAXDOM解析模型事件驱动、流式树形结构、一次性加载内存占用低与文档大小无关高约为文档的 510 倍访问方式只能顺序读取可随机访问任意节点是否可修改只读可增删改并回写适用场景大文件、只需提取部分数据小文件、需要频繁操作节点SAX 的核心接口使用 SAX 需要实现org.xml.sax.ContentHandler接口实际开发中通常继承它的适配器类DefaultHandler只重写关心的方法读到文档开头 →startDocument()读到开始标签xxx→startElement()读到标签中间的文本 →characters()读到结束标签/xxx→endElement()读到文档末尾 →endDocument()XML解析把person标签数据封装成 Person 对象存入 ListXML?xml version1.0 encodingUTF-8? persons person name至尊宝/name age9000/age /person /persons① Person 实体类就是普通 JavaBean存 name 和 age提供 get/setclass Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name name; } public int getAge() { return age; } public void setAge(int age) { this.age age; } }② PersonHandler 继承 DefaultHandler成员变量class PersonHandler extends DefaultHandler { private ListPerson persons; // 存储所有Person对象 private Person person; // 当前正在解析的Person对象 private StringBuilder sb; // 用于拼接标签内的文本内容解决SAX解析中的文本分片问题 }方法 1startDocument () 文档开始时触发整个 XML 刚开始解析的时候执行一次。 适合做初始化创建集合、初始化字符串缓冲区Override public void startDocument() throws SAXException { persons new ArrayList(); // 初始化容器 sb new StringBuilder(); // 初始化字符串缓冲区 }方法 2startElement () 读到【开始标签】xxx触发参数qName标签名比如persons、person、name、age逻辑新标签进来清空文本缓冲区上一个标签的文本不要残留如果标签是person→ new Person 对象准备接收 name、age 数据Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println(qName --解析开始); sb.setLength(0); // 清空缓冲区确保每次新标签内容独立 // 判断当前标签是否为 person若是则创建新的Person对象 if (person.equals(qName)) { person new Person(); } }方法 3characters () 读取标签中间的文本内容⚠️ 重点考点SAX 这个方法可能分多次调用不能在这里直接取值赋值 只做一件事把读到的字符追加到 StringBuilderOverride public void characters(char[] ch, int start, int length) throws SAXException { // 将字符数组中指定范围的内容追加到缓冲区 sb.append(ch, start, length); }方法 4endElement () 读到【结束标签】/xxx触发qName是当前闭合标签名字逻辑先把缓冲区里拼接好的文本拿出来去掉前后空白判断闭合标签如果是/name→ 给 person 设置 name如果是/age→ 给 person 设置 age转数字如果是/person→ 代表这个人信息读完了add 进集合Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println(qName --解析结束); String content sb.toString().trim(); // 去除前后空白字符 // 根据标签名设置对应属性值 if (name.equals(qName)) { person.setName(content); } else if (age.equals(qName)) { if (content.length() 0) { person.setAge(Integer.parseInt(content)); // 转换为整数 } } else if (person.equals(qName)) { // 完成一个完整 person 元素的解析将其加入列表 persons.add(person); } }方法 5endDocument () 整个文档解析完成Override public void endDocument() throws SAXException { System.out.println(------------解析文档结束----------); }向外提供方法 getPersons ()public ListPerson getPersons() { return persons; }③ XmlTest02 主程序负责创建解析器、启动解析SAX 固定 4 步创建 SAXParserFactory 工厂通过工厂拿到 SAXParser 解析器自己写的 Handler 处理器parser.parse (流handler) 开始解析 XMLpublic class XmlTest02 { public static void main(String[] args) { // 1. 获取SAX工厂实例用于创建SAX解析器 SAXParserFactory factory SAXParserFactory.newInstance(); try { // 2. 使用工厂创建SAX解析器对象 SAXParser parser factory.newSAXParser(); // 3. 实例化自定义事件处理器用于处理XML元素的开始、结束及内容 PersonHandler handler new PersonHandler(); // 4. 从类路径加载XML文件流路径为com/server/p.xml InputStream is Thread.currentThread() .getContextClassLoader() .getResourceAsStream(com/server/p.xml); // 5. 若资源流为空说明文件未找到输出错误提示并终止程序 if (is null) { System.err.println(找不到XML文件请检查路径是否正确); return; } // 6. 启动解析过程将输入流和处理器传入触发事件回调 parser.parse(is, handler); // 7. 解析完成后获取处理器中保存的全部Person对象列表 ListPerson persons handler.getPersons(); // 8. 遍历结果并打印每个人的姓名与年龄信息 for (Person p : persons) { System.out.println(p.getName() p.getAge()); } } catch (ParserConfigurationException | SAXException | IOException e) { // 9. 捕获解析过程中可能出现的异常并打印堆栈信息以供调试 e.printStackTrace(); } } }web.xml 解析把多个标签数据封装成对象存入 List下面是一份典型的web.xml配置片段它通过servlet与servlet-mapping两组标签把 Servlet 类与访问路径一一对应起来。?xml version1.0 encodingUTF-8? web-app servlet servlet-namelogin/servlet-name servlet-classcom.shsxt.LoginServlet/servlet-class /servlet servlet servlet-namereg/servlet-name servlet-classcom.shsxt.RegisterServlet/servlet-class /servlet servlet-mapping servlet-namelogin/servlet-name url-pattern/login/url-pattern url-pattern/g/url-pattern /servlet-mapping servlet-mapping servlet-namereg/servlet-name url-pattern/reg/url-pattern /servlet-mapping /web-app可以看到servlet标签负责声明「名字到类」的映射servlet-mapping标签负责声明「名字到访问路径」的映射两者通过servlet-name关联起来。同一个 Servlet 可以配置多个url-pattern例如上面的login就同时映射了/login和/g两个路径。package servlet; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * 使用SAX方式解析web.xml配置文件提取Servlet定义与URL映射关系 */ public class XmlTest02 { public static void main(String[] args) { // 1. 获取SAX解析工厂实例 SAXParserFactory factory SAXParserFactory.newInstance(); try { // 2. 创建SAX解析器对象 SAXParser parser factory.newSAXParser(); // 3. 实例化自定义事件处理器用于处理XML解析过程中的各种事件 WebHandler handler new WebHandler(); // 4. 从类路径中加载web.xml资源流路径为servlet/web.xml InputStream is Thread.currentThread() .getContextClassLoader() .getResourceAsStream(servlet/web.xml); // 5. 判断资源是否加载成功 if (is null) { System.err.println(找不到XML文件请检查路径是否正确); return; } // 6. 启动解析过程将XML流交由处理器处理 parser.parse(is, handler); // 7. 解析完成后获取处理结果并输出 ListEntity entitys handler.getEntitys(); ListMapping mappings handler.getMappings(); System.out.println(共解析到 entitys.size() 个Servlet定义); System.out.println(共解析到 mappings.size() 个URL映射); System.out.println(Servlet列表 entitys); System.out.println(URL映射列表 mappings); } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); } } } /** * 自定义SAX处理器负责响应XML解析过程中的开始文档、开始元素、字符数据、结束元素等事件 */ class WebHandler extends DefaultHandler { private ListEntity entitys; // 存储所有Servlet定义信息 private ListMapping mappings; // 存储所有URL映射关系 private Entity entity; // 当前正在构建的Servlet对象 private Mapping mapping; // 当前正在构建的URL映射对象 private String tag; // 当前正在处理的标签名 private boolean isMapping; // 标记当前是否处于servlet-mapping上下文中 private StringBuilder sb; // 用于拼接标签内的文本内容解决分片问题 /** * 文档开始时初始化容器 */ Override public void startDocument() throws SAXException { entitys new ArrayList(); mappings new ArrayList(); } /** * 遇到开始标签时触发根据标签名称进行不同逻辑处理 */ Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println(qName --解析开始); tag qName; sb.setLength(0); // 清空缓冲区准备接收新内容 // 若是servlet标签创建新的Entity对象 if (servlet.equals(tag)) { entity new Entity(); isMapping false; } // 若是servlet-mapping标签创建新的Mapping对象 else if (servlet-mapping.equals(tag)) { mapping new Mapping(); isMapping true; } } /** * 收集字符数据即标签之间的文本内容通过缓冲区逐步拼接 */ Override public void characters(char[] ch, int start, int length) throws SAXException { sb.append(ch, start, length); } /** * 遇到结束标签时触发根据标签名设置对应属性值并决定是否保存对象 */ Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println(qName --解析结束); String content sb.toString().trim(); // 去除前后空白字符 // 处理servlet-name标签内容 if (servlet-name.equals(qName)) { if (isMapping mapping ! null) { mapping.setName(content); } else if (entity ! null) { entity.setName(content); } } // 处理servlet-class标签内容 else if (servlet-class.equals(qName)) { if (entity ! null !content.isEmpty()) { entity.setClz(content); } } // 处理url-pattern标签内容 else if (url-pattern.equals(qName)) { if (mapping ! null !content.isEmpty()) { mapping.addPattern(content); } } // 结束servlet标签时将完整实体加入集合 if (servlet.equals(qName)) { entitys.add(entity); } // 结束servlet-mapping标签时将完整映射加入集合 else if (servlet-mapping.equals(qName)) { mappings.add(mapping); } tag null; // 清除当前标签状态 } /** * 文档解析完成时调用可做收尾工作 */ Override public void endDocument() throws SAXException { System.out.println(------------解析文档结束----------); } /** * 提供外部访问解析结果的方法 */ public ListMapping getMappings() { return mappings; } public ListEntity getEntitys() { return entitys; } } /** * 封装Servlet定义信息的类 */ class Entity { private String name; private String clz; public String getName() { return name; } public void setName(String name) { this.name name; } public String getClz() { return clz; } public void setClz(String clz) { this.clz clz; } Override public String toString() { return Entity{name name , clz clz }; } } /** * 封装URL映射关系的类 */ class Mapping { private String name; private ListString patterns new ArrayList(); public String getName() { return name; } public void setName(String name) { this.name name; } public void addPattern(String pattern) { patterns.add(pattern); } public ListString getPatterns() { return patterns; } Override public String toString() { return Mapping{name name , patterns patterns }; } }
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑