xmlinputfactory.newfactory() 可能返回 null 或抛 factoryconfigurationerror,因jvm不保证有stax实现;jdk 8自带弱实现,openjdk 17+已移除,须显式引入woodstox等依赖并用newinstance指定类名。

为什么 XMLInputFactory.newFactory() 有时返回 null 或抛 FactoryConfigurationError
因为 JVM 默认不保证有可用的 StAX 实现,newFactory() 会尝试加载 SPI(META-INF/services/javax.xml.stream.XMLInputFactory)里声明的类,但若 classpath 没有实现(比如只有 JDK 8 且没额外加 Woodstox/Aalto),就可能失败。
常见错误现象:java.util.ServiceConfigurationError 或返回 null 后调用 createXMLEventReader 时抛 NullPointerException。
- JDK 8 自带的默认实现较弱(
com.sun.org.apache.xerces.internal.impl.XMLInputFactoryImpl),部分特性(如IS_NAMESPACE_AWARE)不支持或行为异常 - OpenJDK 17+ 已移除内置 StAX 实现,不显式引入依赖必报错
- 别依赖无参
newFactory()—— 它不校验可用性,只“尽力而为”
如何安全创建 XMLInputFactory 实例
显式指定实现类名是最可控的方式,尤其在多环境部署时。优先选 Woodstox(稳定、功能全、性能好),其次 Aalto(轻量、流式强)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 添加 Maven 依赖:
com.fasterxml.woodstox:woodstox-core(注意不是stax2-api,那是接口层) - 用
XMLInputFactory.newInstance("com.ctc.wstx.stax.WstxInputFactory", null),第二个参数是ClassLoader,传null表示用当前线程上下文类加载器 - 创建后立即设置关键属性,比如
factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true),避免后续解析出错 - 不要在静态块里缓存工厂实例再复用——Woodstox 的 factory 是线程安全的,但某些老实现(如早期 Sun 实现)不是;稳妥起见,每次解析前 new 一个,或用 ThreadLocal 封装
XMLInputFactory 常见配置项和坑点
StAX 解析行为高度依赖工厂配置,设错一个布尔值可能导致整个 XML 结构读错或跳过内容。
-
XMLInputFactory.IS_COALESCING:设为true会把相邻文本合并,但若 XML 里有混合内容(text + element),可能丢失空格或换行——多数业务不需要它,保持false更安全 -
XMLInputFactory.SUPPORT_DTD:默认true,但禁用 DTD 可防 XXE 攻击;设false后遇到直接抛 <code>XMLStreamException -
XMLInputFactory.IS_VALIDATING:Java 内置实现根本不支持 true,设了也无效;验证靠外部 Schema,别在这儿折腾 - 路径或资源名含中文/空格?用
FileInputStream手动打开再传给createXMLEventReader(InputStream),别直接传File或路径字符串——工厂内部的 URI 解析容易挂
Woodstox vs JDK 内置实现的关键差异
不是所有 XMLInputFactory 都能跑通同一段代码。Woodstox 对命名空间、CDATA、reporting 错误位置等更严格也更准;JDK 自带的常静默吞掉问题。
- 解析
<root xmlns="http://a"><child></child></root>:JDK 实现可能让getNamespaceURI()在START_ELEMENT事件里返回空字符串;Woodstox 返回正确 URI - 遇到未闭合标签(如
<tag>):JDK 报错位置常指向文件末尾,Woodstox 能准确定位到第几行第几列</tag> - 性能上,Woodstox 在大文件流式解析时内存占用低 30%+,尤其当频繁调用
nextTag()或跳过元素时 - 如果项目已用 Jackson XML(
jackson-dataformat-xml),它底层就是 Woodstox,复用同一份依赖即可,不用额外引
真正麻烦的不是怎么创建工厂,而是不同 JDK 版本 + 不同依赖组合下,newFactory() 返回的对象行为不一致——测试时跑得通,上线换容器就崩,这种隐性差异最耗时间。










