BeanMap 是 Commons Collections 提供的特殊 Map 實作,能將任意 JavaBean 自動轉換為 Map,鍵為屬性名,值為屬性值,支援雙向讀寫、型別轉換、複製與事件通知,極適合表單綁定、配置映射、泛型資料存取等場景。
public class BeanMap extends AbstractMap implements Cloneable {
private transient Object bean; // 底層 JavaBean 實例
private transient HashMap readMethods = new HashMap(); // 屬性名 → getter Method
private transient HashMap writeMethods = new HashMap(); // 屬性名 → setter Method
private transient HashMap types = new HashMap(); // 屬性名 → 屬性 Class
// 預設型別轉換器:字串 → 基本型別包裝類
public static HashMap defaultTransformers = new HashMap();
}
private void initialise() {
BeanInfo info = Introspector.getBeanInfo(bean.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
String name = pd.getName();
if (pd.getReadMethod() != null) readMethods.put(name, pd.getReadMethod());
if (pd.getWriteMethod() != null) writeMethods.put(name, pd.getWriteMethod());
types.put(name, pd.getPropertyType());
}
}
BeanMap(Object bean) 呼叫 initialise() 掃描所有可讀寫屬性。Introspector 標準 JavaBeans 內省機制,零配置自動發現。public Object get(Object name) {
Method getter = getReadMethod(name);
return (getter != null) ? getter.invoke(bean, NULL_ARGUMENTS) : null;
}
public Object put(Object name, Object value) {
Method setter = getWriteMethod(name);
if (setter == null) throw new IllegalArgumentException("No property: " + name);
Object old = get(name);
Object[] args = createWriteMethodArguments(setter, value); // 型別轉換
setter.invoke(bean, args);
firePropertyChange(name, old, get(name)); // 觸發屬性變更事件
return old;
}
get/put 透過反射動態調用 getter/setter。createWriteMethodArguments 自動處理型別轉換(見下)。PropertyChangeListener 機制,支援綁定框架。protected Object convertType(Class targetType, Object value) {
// 1. 嘗試單參數建構子:new TargetType(value)
// 2. 失敗則查找 defaultTransformers(字串 → 基本型別包裝類)
// 3. 仍失敗則回傳原值
}
String 互轉。Transformer 擴充轉換邏輯(如日期解析)。public Object clone() {
BeanMap copy = (BeanMap) super.clone();
Object newBean = bean.getClass().newInstance(); // 新實例
copy.setBean(newBean);
// 複製所有可讀寫屬性值
for (Object key : readMethods.keySet()) {
if (getWriteMethod(key) != null) copy.put(key, get(key));
}
return copy;
}
clear() 則重新實例化底層 Bean 回初始狀態。protected static class MyMapEntry extends AbstractMapEntry {
private BeanMap owner;
public Object setValue(Object value) {
Object old = owner.get(getKey());
owner.put(getKey(), value);
super.setValue(owner.get(getKey()));
return old;
}
}
entry.setValue() 直接委派 BeanMap.put(),保持同步。| 場景 | 用法 |
|---|---|
| 表單資料綁定 | HTTP 參數 Map → BeanMap → 自動填充 JavaBean |
| 配置物件化 | Properties → BeanMap → 強型別配置 Bean |
| 泛型資料存取層 | 以 Map 介面統一存取不同 Bean,支援序列化/複製 |
| 測試資料構建 | 快速建立 Bean 並透過 Map 介面批量設值 |
public class User {
private String name; private int age;
// getter/setter...
}
User user = new User();
BeanMap map = new BeanMap(user);
map.put("name", "Alice");
map.put("age", "25"); // 字串自動轉 int
System.out.println(user.getName()); // Alice
System.out.println(user.getAge()); // 25
// 複製
BeanMap copy = (BeanMap) map.clone();
copy.put("age", 26);
System.out.println(user.getAge()); // 25 (原物件未變)
System.out.println(((User)copy.getBean()).getAge()); // 26
BeanMap 以標準內省機制橋接 JavaBean 與 Map 世界,提供型別安全、事件驅動、可複製的雙向存取層。掌握其轉換機制與複製語義,能大幅簡化表單處理、配置管理與測試資料構建等常見任務。