展示如何透過 dom4j 解析 XML 設定檔,結合反射機制實現 抽象工廠模式,動態建立 Service 與 DAO 兩大產品族的實例,並以單例快取管理物件生命週期。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<service-class>
<service id="com.xxjstgb.drp.basedata.manager.ItemManager"
class="com.xxjstgb.drp.basedata.manager.ItemManagerImpl" />
<service id="com.xxjstgb.drp.flowcard.manager.FlowCardManager"
class="com.xxjstgb.drp.flowcard.manager.impl.FlowCardManagerImpl" />
</service-class>
<dao-class>
<dao id="com.xxjstgb.drp.basedata.dao.ItemDao"
class="com.xxjstgb.drp.basedata.dao.ItemDao4OracleImpl" />
<dao id="com.xxjstgb.drp.flowcard.dao.FlowCardDao"
class="com.xxjstgb.drp.flowcard.dao.impl.FlowCardDaoImpl" />
</dao-class>
</beans>
package com.xxjstgb.drp.util;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import java.util.*;
public class BeanFactory {
private static final BeanFactory INSTANCE = new BeanFactory();
private static final String CONFIG_FILE = "beans-config.xml";
private final Document doc;
private final Map<String, Object> serviceCache = new HashMap<>();
private final Map<String, Object> daoCache = new HashMap<>();
private BeanFactory() {
try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(CONFIG_FILE)) {
if (is == null) throw new RuntimeException("Config not found: " + CONFIG_FILE);
doc = new SAXReader().read(is);
} catch (Exception e) {
throw new RuntimeException("Init BeanFactory failed", e);
}
}
public static BeanFactory getInstance() { return INSTANCE; }
// 取得 Service 實例(介面 Class 作為鍵)
public synchronized Object getServiceObject(Class<?> iface) {
String key = iface.getName();
if (serviceCache.containsKey(key)) return serviceCache.get(key);
Element el = (Element) doc.selectSingleNode("//service[@id='" + key + "']");
if (el == null) throw new RuntimeException("Service config missing: " + key);
String implClass = el.attributeValue("class");
Object instance = createInstance(implClass);
serviceCache.put(key, instance);
return instance;
}
// 取得 DAO 實例
public synchronized Object getDaoObject(Class<?> iface) {
String key = iface.getName();
if (daoCache.containsKey(key)) return daoCache.get(key);
Element el = (Element) doc.selectSingleNode("//dao[@id='" + key + "']");
if (el == null) throw new RuntimeException("DAO config missing: " + key);
String implClass = el.attributeValue("class");
Object instance = createInstance(implClass);
daoCache.put(key, instance);
return instance;
}
private Object createInstance(String className) {
try {
return Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Instantiate failed: " + className, e);
}
}
// 測試主程式
public static void main(String[] args) {
ItemManager itemMgr = (ItemManager) getInstance().getServiceObject(ItemManager.class);
ItemDao itemDao = (ItemDao) getInstance().getDaoObject(ItemDao.class);
System.out.println("itemManager: " + itemMgr);
System.out.println("itemDao: " + itemDao);
}
}
| 優點 | 建議強化 |
|---|---|
| 設定與程式碼解耦,換實作無需改碼 | 加入 Bean 生命週期管理 (init/destroy 方法) |
| 單例快取避免重複建立 | 支援 Prototype 作用域(每次新建) |
| XPath 查詢靈活 | 改用 DOM4J 2.x / StAX 提升解析效能 |
| 同步方法保證執行緒安全 | 改用 ConcurrentHashMap + Double-Check Locking 降低鎖爭用 |
實務專案建議直接採用成熟 IoC 容器(Spring / Guice / Dagger),提供:
本範例適合教學理解 IoC 核心原理,或極輕量環境無法引入框架時參考。