下面的回答假設你的問題短語「Java自定義控制」是指你開發的JSF組件;在XPages中,術語「自定義控件」通常指自定義控件設計元素的一個實例,這是IBM對「複合組件」的JSF概念的實現。
你說,最初成分表現如預期,但未能在後續的請求。這通常表示該組件的restoreState
和saveState
方法並沒有得到很好的貫徹。
當爲應用程序啓用默認序列化選項時,所有組件狀態在每個請求結束時寫入磁盤,並在下一個開始時讀回到內存中。這兩個操作的處理,分別由每種組分的saveState
和restoreState
方法。
例如,假設您定義了一個用於將HTML canvas標籤添加到XPage的組件,並決定支持與該元素關聯的手勢和觸摸事件。所以,你的組件類會包含字段存儲綁定到這些事件的任何代碼:
private String ongesturechange;
private String ongestureend;
private String ongesturestart;
private String ontouchcancel;
private String ontouchend;
private String ontouchmove;
private String ontouchstart;
每個這些字段通常會再有一個相關的「吸氣」和「二傳手」的方法:
public String getOngesturechange() {
return getStringProperty("ongesturechange", this.ongesturechange);
}
public void setOngesturechange(String ongesturechange) {
this.ongesturechange = ongesturechange;
}
當該組件的一個實例被初始化,與爲該組件實例定義的每個屬性關聯的「setter」方法將被傳遞爲該屬性定義的值。然後,對於初始頁面請求的其餘部分,每個已定義屬性的專用字段將存儲已設置的值。在請求結束時,saveState
方法將這些字段的值寫入磁盤。一個典型的saveState
方法看起來類似如下:
@Override
public Object saveState(FacesContext context) {
Object[] properties = new Object[8];
int idx = 0;
properties[idx++] = super.saveState(context);
properties[idx++] = this.ongesturechange;
properties[idx++] = this.ongestureend;
properties[idx++] = this.ongesturestart;
properties[idx++] = this.ontouchcancel;
properties[idx++] = this.ontouchend;
properties[idx++] = this.ontouchmove;
properties[idx++] = this.ontouchstart;
return properties;
}
到super.saveState()
調用執行相同的方法,但是使用在父類中定義的方法的版本。因此,每個組件的磁盤表示本質上是一個嵌套數組:層次結構中的每個層都存儲它從其父類繼承的所有屬性到數組的第一個元素中,然後將其定義的所有屬性存儲在其他數組元素中。
當組分樹在後續請求恢復,各組件使用其restoreState
方法來重構其所有字段的值。一個典型的restoreState
方法類似於以下內容:
@Override
public void restoreState(FacesContext context, Object state) {
Object[] properties = (Object[]) state;
int idx = 0;
super.restoreState(context, properties[idx++]);
this.ongesturechange = ((String) properties[idx++]);
this.ongestureend = ((String) properties[idx++]);
this.ongesturestart = ((String) properties[idx++]);
this.ontouchcancel = ((String) properties[idx++]);
this.ontouchend = ((String) properties[idx++]);
this.ontouchmove = ((String) properties[idx++]);
this.ontouchstart = ((String) properties[idx++]);
}
此分層讀取在磁盤上的數據回覆於:每個類將一組屬性的父類的,然後分配剩餘的數組元素中的字段他們與保存組件狀態的時間有關。
這一過程提供了一種簡單的方式來維護跨請求組件狀態 - 繼承的每一層只需要與該層定義了新的屬性本身的關注 - 但這些國家的保養方法很容易忘記實現。如果組件實現中省略了任何一種方法,那麼頁面會在後續請求中「忘記」屬性值,因爲它們從未寫入磁盤,或者未被加載回內存中,或者兩者都未加載。
假設這是問題的根本原因,當組件位於默認值爲(false
)值爲keepComponents
的對話框中時,問題不會發生的原因是默認的對話行爲是將其子項從組件樹完全在對話框關閉時。這種行爲是出於性能方面的原因:理論上存儲服務器端組件的表示只存在於用戶當前沒有與之進行交互的對話框中沒有任何好處。當對話框再次打開時,使用原始屬性值創建每個子組件的新的實例。在這種情況下,組件不會保存其狀態,因爲每次使用它時都會創建一個新實例。但是,如果通知對話將子對象保留在組件樹中,那麼現在組件必須正確維護其自己的狀態......否則其屬性值將在每個請求結束時丟棄,並且後續請求不知道以前的值。
總之,是的,你顯示的數據應該在一個bean(或數據源)高速緩存,如果數據是不可能足夠的請求之間的每一個事件再次變更證明獲得的數據。但是你描述的特定行爲的原因很可能是因爲你的組件實現不能正確維護自己的狀態。
代碼示例請 – stwissel 2013-04-05 10:05:24