首页/新闻资讯/正文详情

11手写服务器HTTP Server

发布时间:2026/11/6 5:15:57 来源:云帆数科 栏目:资讯中心
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 }; } }

相关推荐

储能主控通信稳不住?聊聊 RK3568B2 平台总线高抗干扰设计
储能主控通信稳不住?聊聊 RK3568B2 平台总线高抗干扰设计

摘要:储能舱内 PCS、电感、大功率回路干扰复杂,CAN、RS485、以太网丢包、误码、断连是 BAMS/EMS 项目高频痛点。本文基于 RK3568B2 硬件平台,拆解储能场景下总线隔离、防护、PCB 抗干扰设计思路,解决储能现场通信不稳难题。做储能… · 2026/11/6 5:15:57

开源深拆:权重经济学与商品化地缘
开源深拆:权重经济学与商品化地缘

第 12 篇蒸馏写过一个事实:开源模型是闭源能力的下游。开源深拆要回答的是产业问题:**既然权重可以自由下载,为什么还有人付钱给闭源 API?**答案藏在"开源"二字的会计学里。 一、权重不是软件 开源模型与传统开源软件有… · 2026/11/6 5:14:57

1.24 亿播放的内容做对了什么:三条可复用的规律
1.24 亿播放的内容做对了什么:三条可复用的规律

1.24 亿播放的内容做对了什么:三条可复用的规律 做技术的人看内容爆款,容易归因成"运气"或者"审美"。但一支成本几千块、没明星没特效的国风 MV,全网播到 1.24 亿(抖音 7500 万、视频号 4500 万、小红书 300 … · 2026/11/6 5:14:57

特种电工作业实操模拟考试题库,网上查询考题别再踩坑
特种电工作业实操模拟考试题库,网上查询考题别再踩坑

特种电工作业实操模拟考试题库,网上查询考题别再踩坑 怕考不过白交培训费?特种电工作业实操考试别再靠运气了,网上查题库、刷真题,才能稳稳拿证!… · 2026/11/6 19:08:53

广水考电工证怎么考不挂科?郑州报考避坑指南来了
广水考电工证怎么考不挂科?郑州报考避坑指南来了

广水考电工证怎么考不挂科?郑州报考避坑指南来了 实操考试心里没底怕挂科?你不是一个人在战斗。广水考电工证是很多电工人从业路上的“硬门槛”,但很多人都在报考过程中踩坑,尤其在郑州,因为信息不对称、培训质量参差不齐、考试流程不熟悉,导致很多人白… · 2026/11/6 19:08:53

中山考电工证地址值不值得考?急需特种作业证才能上岗找工作
中山考电工证地址值不值得考?急需特种作业证才能上岗找工作

中山考电工证地址值不值得考?急需特种作业证才能上岗找工作 你现在是不是正被“急需特种作业证才能上岗找工作”这个问题折磨?找工作被拒,就因为没电工证?别急,这篇文章帮你搞定【中山考电工证地址】的问题,顺便告诉你这个证值不值得考。… · 2026/11/6 19:08:52

吐鲁番电工证怎么考含金量高还是白花钱
吐鲁番电工证怎么考含金量高还是白花钱

吐鲁番电工证怎么考含金量高还是白花钱 怕考不过白交培训费,这是很多准备报考吐鲁番电工证的学员最担心的问题。特别是在现在这个对特种作业人员监管越来越严格的时代,没有电工证就上岗,不仅会被处罚,还可能面临法律责任。电工证的 含金量… · 2026/11/6 19:08:52

息县考电工证在哪里值不值得考
息县考电工证在哪里值不值得考

息县考电工证在哪里值不值得考 证书过期了不知道怎么复审延期?你是电工行业的从业者,或者正在考虑转行进入这个领域,那你一定遇到过这样的困惑。尤其是在息县,电工证作为从事电力作业的法定凭证,其重要性不言而喻。但很多从业人员对电工证的办理流程、有… · 2026/11/6 19:08:52

红安电工证考试怎么报名?证书过期了不知道怎么复审延期怎么办
红安电工证考试怎么报名?证书过期了不知道怎么复审延期怎么办

红安电工证考试怎么报名?证书过期了不知道怎么复审延期怎么办 证书过期了不知道怎么复审延期?这是很多电工从业者最头疼的问题。特别是红安地区的电工朋友,如果电工证没及时办理复审,不仅影响工作机会,甚至可能被处罚。今天就从… · 2026/11/6 19:07:50

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23

三菱PLC数据采集到数据库的时序设计:Python+MC协议实战
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

ASIC主模式RTL设计:状态机与三态驱动的流片级实现
ASIC主模式RTL设计:状态机与三态驱动的流片级实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

STM32嵌入式C++开发:GDB调试与Renode仿真实战指南
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码