java中用带预读能力的自定义迭代器控制字节流读取节奏,配合状态机管理报文语义阶段:迭代器提供peek()/next()支持窥探与消费,状态机按wait_length、read_payload等阶段封装处理逻辑,通过parsingcontext统一桥接,实现低内存、高可维护的流式解析。

在 Java 中,将迭代器模式与状态机模式结合用于复杂业务报文的流式解析,核心在于:用迭代器控制字节流的“读取节奏”,用状态机管理报文结构的“语义阶段”。两者分工明确——迭代器负责“怎么读”,状态机负责“当前在读什么、下一步该做什么”。
迭代器提供可控的字节流预读能力
流式解析不能一次性加载全部数据,必须边读边判。标准 Iterator<byte></byte> 不支持回退或窥探(peek),因此需自定义带预读能力的迭代器:
- 封装原始输入流(如
InputStream或ByteBuffer)为惰性字节序列 - 内部维护一个长度为 1 的双端队列(
Deque<byte></byte>)作为 lookahead 缓冲区 - 暴露
peek()方法:若缓冲为空则从底层流取一个字节填入;next()则返回并清空该字节 - 这样,解析逻辑可在不消耗字节的前提下判断下一个字节是否符合当前状态预期(例如是否是字段分隔符、长度标识位等)
状态机定义报文解析的生命周期阶段
业务报文(如自定义二进制协议、TLV 结构、分段长度前缀帧)天然具有状态流转特征。例如一个典型长度前缀报文:
- WAIT_LENGTH:等待读取 2 字节长度字段
- READ_PAYLOAD:根据已知长度,连续读取指定字节数
- PARSE_FIELDS:对 payload 按字段定义逐个解码(此时可能嵌套子状态)
- DONE 或 ERROR:成功完成或遇到非法字节终止
每个状态封装自己的处理逻辑(如 “在 WAIT_LENGTH 下,调用 peek() 确认还有 2 字节可用,再 next() 两次取出长度值”),状态切换由当前字节和上下文共同触发。
两者协同的关键接口设计
不要让迭代器持有状态,也不让状态直接操作流——通过统一上下文桥接:
- 定义
ParsingContext类,持有一个LookaheadIterator实例和当前ParseState - 每个
ParseState是接口或抽象类,声明process(Context ctx)方法 -
process内部只调用ctx.iterator().peek()和ctx.iterator().next(),根据返回值决定是否切换状态(如ctx.setState(new ReadPayloadState(length))) - 主循环只需反复执行
context.getState().process(context),直到状态变为DONE或ERROR
实际解析中的典型配合场景
以解析一个“4 字节魔数 + 2 字节版本 + 变长 JSON body(长度前缀)”的报文为例:
- 初始状态
ReadMagicState:用peek()连续检查 4 字节是否匹配魔数值;匹配则next()消费,转入ReadVersionState -
ReadVersionState:读取 2 字节版本号,验证范围,成功后转入ReadLengthState -
ReadLengthState:读取 4 字节长度字段(大端),校验是否超限;合法则创建ReadBodyState(length)并切换 -
ReadBodyState:在process中循环调用next()直到读满 length 字节,然后交由 JSON 解析器处理,完成后设为DONE
整个过程无需缓存整帧,内存占用恒定;状态切换清晰可测,新增报文类型只需扩展状态类,不改动迭代器或主循环。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











