從 .ini/.properties 到 XML,配置文件演進體現三大趨勢:**結構化表達力、層級命名空間、工具鏈生態成熟**。Tomcat server.xml、J2EE web.xml、Spring applicationContext.xml 確立了 XML 配置的事實標準地位。
| 維度 | DOM | SAX |
|---|---|---|
| 模型 | 樹狀結構全量載入內存 | 事件流式處理,不建樹 |
| 內存佔用 | O(N),大文件易 OOM | O(1),適合超大文件 |
| 隨機訪問 | 支持 XPath、DOM API | 單向流,需自行維護上下文 |
| 編程複雜度 | 低,直觀 | 中,需實現 Handler 回調 |
| 適用場景 | 小型配置、需隨機訪問、文檔修改 | 大型配置、只讀解析、內存受限環境 |
本文採用 **SAX (JAXP 標準 API)**,兼容 JDK 1.4+,無第三方依賴。
<?xml version="1.0" encoding="UTF-8"?>
<myenv>
<datasource>
<dbhost>localhost</dbhost>
<dbname>sqlname</dbname>
<dbuser>username</dbuser>
<dbpassword>password</dbpassword>
</datasource>
</myenv>
放置於 WEB-INF/classes/myenv.xml,類加載路徑可直接訪問。
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.Properties;
public class ConfigParser extends DefaultHandler {
private Properties props = new Properties();
private String currentName;
private StringBuffer currentValue = new StringBuffer();
public Properties getProps() { return props; }
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
currentValue.setLength(0); // 重置緩衝區
this.currentName = qName; // 記錄當前元素名
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
currentValue.append(ch, start, length); // 累加文本內容
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
props.put(qName.toLowerCase(), currentValue.toString().trim()); // 存入 Properties
}
}
設計要點:
DefaultHandler 避免實現所有空方法StringBuffer 復用避免頻繁創建字符串import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.net.URL;
import java.util.Properties;
public class ParseXML {
private Properties props;
public Properties getProps() { return props; }
public void parse(String filename) throws Exception {
ConfigParser handler = new ConfigParser();
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false); // 不處理命名空間
factory.setValidating(false); // 不驗證 DTD/Schema
SAXParser parser = factory.newSAXParser();
// 類加載器獲取資源路徑(適用於 WEB-INF/classes)
URL confURL = ParseXML.class.getClassLoader().getResource(filename);
if (confURL == null) throw new RuntimeException("Config not found: " + filename);
parser.parse(confURL.toString(), handler);
this.props = handler.getProps();
}
}
public class ConfigTest {
public static void main(String[] args) throws Exception {
ParseXML parser = new ParseXML();
parser.parse("myenv.xml");
Properties props = parser.getProps();
System.out.println("dbhost: " + props.getProperty("dbhost"));
System.out.println("dbuser: " + props.getProperty("dbuser"));
// 輸出:dbhost: localhost, dbuser: username
}
}
| 需求 | 實現方案 |
|---|---|
| 巢狀結構解析 | 維護 Stack 記錄元素路徑,鍵名用 parent.child 格式 |
| 屬性值解析 | 在 startElement 中讀取 attrs.getValue("name") |
| 類型轉換 | 封裝 getInt()、getBoolean() 等輔助方法 |
| 多配置文件合併 | 多次 parse() 合併 Properties,後載入覆蓋優先 |
| 熱加載 | WatchService 監聽文件變更,自動重新解析 |
SAX 解析器以**流式處理、常數內存、零依賴**三大優勢,成為 Java 讀取 XML 配置的經典選擇。掌握 DefaultHandler 三大回調 (startElement/characters/endElement) 與 SAXParserFactory 配置,即可構建穩健的配置加載體系。雖然現代框架提供更高級抽象,但理解底層機制對排查配置加載異常、設計輕量級組件仍具核心價值。