JavaBean 是 Java 生態的基石,無處不在:Spring Bean、JPA 實體、Struts ActionForm、JSP 模型、Hibernate/MyBatis 映射對象。但「私有字段 + get/set」的表象下,JavaBeans Spec 1.01 (114 頁) 定義了嚴格的命名推導規則。IDE 自動生成的代碼往往違反規範,導致框架報「找不到屬性」。本文徹底講透規範核心,解決實戰疑難雜症。
屬性 是邏輯概念,非物理字段。由存取器模式推導而來:
• 可讀屬性:存在getXxx()/isXxx()(boolean)
• 可寫屬性:存在setXxx()
• 屬性名 = 存取器方法名去除 get/set/is 後,首字母小寫(特殊規則見下)
| 方法名 | 推導屬性名 | 說明 |
|---|---|---|
| getName() | name | 標準 |
| setName(String) | name | 標準 |
| isActive() | active | boolean 類型優先 is |
| getURL() | uRL | ⚠️ 非預期!見 2.2 |
規則:若方法名去除 get/set/is 後,前兩字母均為大寫,則屬性名不轉換首字母,保持原樣。
| 方法名 | 去除前綴 | 前兩字母 | 屬性名 | IDE 常誤生成 |
|---|---|---|---|---|
| getURL() | URL | U、R (大寫) | URL | getUrl() → uRL ❌ |
| setCPUId() | CPUId | C、P (大寫) | CPUId | setCpuId() → cPUId ❌ |
| getX() | X | 單字母 | X | getX() → x ✅ |
| getName() | Name | N、a (第二小寫) | name | 標準規則 |
| getSName() | SName | S、N (大寫) | SName | getSName() → sName ❌ |
慘痛教訓:字段
private String sName;(匈牙利命名殘留),IDE 生成getSName()/setSName(),規範推導屬性名為 SName,框架查找sName失敗 → 「找不到屬性 sName 的 getter/setter」。
修正:字段改為name或fullName,或手動修正方法為getsName()/setsName()(極不推薦)。
除標準 get/set 外,索引屬性支持帶下標訪問:
private OrderItem[] orderItems;
// 標準存取器
public OrderItem[] getOrderItems() { ... }
public void setOrderItems(OrderItem[] items) { ... }
// 索引存取器(可選,但框架常依賴)
public OrderItem getOrderItem(int index) { ... }
public void setOrderItem(int index, OrderItem item) { ... }
框架支持:Struts、Spring、Hibernate、MyBatis 均支持索引屬性綁定(如 orderItems[0].productName)。
isXxx(),其次 getXxx()isXxx() 讀取 boolean/Boolean,setXxx() 寫入isActive() 和 getActive() 返回不同值 → 不確定行為框架(Commons BeanUtils、Struts、Spring、EL 表達式)通過點記法導航對象圖:
| 表達式 | 含義 |
|---|---|
| user.name | user.getName() |
| user.address.city | user.getAddress().getCity() |
| users[0].name | users.get(0).getName() (List/數組) |
| map['key'].value | map.get("key").getValue() (Map) |
| items[1].price | items.get(1).getPrice() |
<!-- 顯示 stocks 列表第 2 個元素的 code 屬性 -->
<html:text property="stocks[1].code" />
<!-- 提交自動填充到對應位置 -->
<form action="/save">
<html:text property="user.address.street" />
<html:text property="orderItems[0].quantity" />
</form>
userName 而非 sUserName/strName)isEnabled()、setEnabled(),禁用 getEnabled()getX() 和 isX() 除非語義不同@Data 生成 getUrl() 導致屬性名 url,但字段 URL 推導屬性 URL → 不一致。顯式聲明 @Getter/@Setter 指定方法名 或重命名字段Serializable、聲明 serialVersionUID、transient 排除敏感/非持久字段
BeanInfo info = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
System.out.printf("Property: %-20s Read: %-15s Write: %-15s%n",
pd.getName(),
pd.getReadMethod() != null ? pd.getReadMethod().getName() : "none",
pd.getWriteMethod() != null ? pd.getWriteMethod().getName() : "none");
}
運行此代碼,對比輸出與預期屬性名,即可驗證 Bean 是否符合規範。
JavaBean 規範看似枯燥,實則是框架互操作的契約。
核心口訣:字段語義化命名 → 存取器標準生成 → 規範推導屬性名 → 框架無縫綁定。
遇到「找不到屬性」異常,首查命名推導、次查存取器成對、再查索引/導航路徑。
遵循規範,是對框架作者、維護者、未來的自己最大的尊重。