
本文介绍一种轻量、可维护的方式,通过 dom 解析 xml 文件,按 `
在实际企业级 Web 应用(如基于 JSP + Servlet 的老系统)中,将 SQL 查询与业务逻辑解耦、外置到 XML 配置文件是常见且推荐的做法。面对类似 flussoVltmensile 这类按描述触发不同操作的需求,不建议硬编码 if (desc.equals("Flusso VLT mensile")) 多层判断——既难以维护,又违背开闭原则。
推荐采用 标准 DOM 解析 + XPath 表达式 方案:简洁、零依赖(JDK 自带)、语义清晰、调试直观。以下是完整实现步骤:
✅ 1. 加载并解析 XML 文件
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class XmlQueryLoader {
private static final String XML_PATH = "/WEB-INF/queries.xml"; // 推荐放在 classpath 或 webapp 受保护路径
public static Document loadXml() throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(XmlQueryLoader.class.getResourceAsStream(XML_PATH));
}
}✅ 2. 根据 description 值精准定位 item 节点
使用 XPath 可一行定位目标节点,无需遍历所有
public class QueryConfig {
private final Document doc;
public QueryConfig(Document doc) {
this.doc = doc;
}
public QueryInfo findByDescription(String description) throws Exception {
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = String.format(
"//item[description/@value='%s']",
description.replace("'", "\\'") // 简单转义单引号,生产环境建议用 XPathExpression + 参数化
);
NodeList nodes = (NodeList) xpath.compile(expression)
.evaluate(doc, XPathConstants.NODESET);
if (nodes.getLength() == 0) {
throw new IllegalArgumentException("No item found for description: " + description);
}
return parseItem(nodes.item(0));
}
private QueryInfo parseItem(org.w3c.dom.Node itemNode) {
String id = itemNode.getAttributes().getNamedItem("id").getNodeValue();
String connectionName = ((org.w3c.dom.Node) itemNode
.getElementsByTagName("connection").item(0))
.getAttributes().getNamedItem("name").getNodeValue();
String filename = ((org.w3c.dom.Node) itemNode
.getElementsByTagName("filename").item(0))
.getAttributes().getNamedItem("value").getNodeValue();
String selectSql = getTextContent(itemNode, "select");
String updateSql = getTextContent(itemNode, "update");
return new QueryInfo(id, connectionName, filename, selectSql, updateSql);
}
private String getTextContent(org.w3c.dom.Node parent, String tagName) {
return parent.getElementsByTagName(tagName).item(0).getTextContent().trim();
}
}
// 封装查询元数据
public class QueryInfo {
public final String id, connectionName, filename, selectSql, updateSql;
public QueryInfo(String id, String connectionName, String filename, String selectSql, String updateSql) {
this.id = id;
this.connectionName = connectionName;
this.filename = filename;
this.selectSql = selectSql;
this.updateSql = updateSql;
}
}✅ 3. 在 Servlet/JSP 控制器中调用(示例)
// 假设从 JSP 提交的参数为:String desc = request.getParameter("description");
try {
Document doc = XmlQueryLoader.loadXml();
QueryConfig config = new QueryConfig(doc);
QueryInfo qi = config.findByDescription(desc); // 如 "Flusso VLT mensile"
// 后续操作:执行查询、更新或返回下载文件名
System.out.println("SQL to execute: " + qi.selectSql);
System.out.println("Download filename: " + qi.filename); // → "flussoVltmensile"
} catch (Exception e) {
log.error("Failed to load query config", e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}⚠️ 注意事项与最佳实践
- 安全性:XPath 表达式中拼接用户输入需谨慎。生产环境应改用 XPathExpression 配合 XPathVariableResolver 实现参数化,或先校验 description 是否在白名单内(如枚举预加载)。
- 性能优化:XML 文件通常静态不变,建议在应用启动时一次性解析并缓存 Document 对象(如 Spring 中声明为 @Bean),避免每次请求重复 IO 和解析。
- 错误处理:明确区分“配置缺失”(应报 500)和“用户选错描述”(应友好提示 400),提升可维护性。
- 替代方案说明:JAXB 确实可行,但需额外定义 POJO + 注解,对简单结构略显笨重;SAX 更省内存但编码复杂度高;而 DOM + XPath 在可读性、开发效率与功能完备性之间取得了极佳平衡。
综上,该方案以最小学习成本达成高可维护性目标——新增一个查询,只需在 XML 中追加
立即学习“Java免费学习笔记(深入)”;










