2009年4月30日 星期四

[Struts2] 深入探討 OGNL

在之前我們已經知道 OGNL expression 可以幫助我們將 data 在網頁上與後端 Javabean 之間作搬移與轉換型態,而且我們也瞭解到我們的 Actions 會被放在 ValueStack 中讓 OGNL 可以存取。現在我們要告訴你,OGNL 可以存取任何集合的物件,而 ValueStack 只是其中一個集合物件而已!更完整的來說,OGNL 是用來存取 ActionContext 中的 data,而 ActionContext 中包含了 ValueStack。

ActionContext
ActionContext 儲存了 Struts2 framework 中所有的 data,這些 data 包含 ValueStack、Request、Session 與 Application 等。而所有的 OGNL 都會到 ActionContext 中查詢 data,不過如果我們沒有指定 OGNL 要到哪裡存取資料的話,OGNL 預設是到 ValueStack 中進行存取。所謂沒有指定的意思是,如果我們撰寫的 OGNL 為:user.age,這就是採用 OGNL 預設的存取位置。如下圖。
在這裡所謂的 Context 不是指環境,而是一種有 container 概念的物件,主要是蒐集整個 framework 在執行時所需要的 data 與 resources。所有的 OGNL 都會先選擇所要存取的初始地點,如果沒有指定,就是到 ValueStack 中存取。如果我們將之前的 User 物件儲存在 session 中,那我們要存取 session 中 user 的 age,那我們就要改用:#session['user'].age。我們採用 # 開頭並且指定我們要存取的位置為 session,因為 session 是一個 Map 物件(回想我們之前所使用 SessionAware interface 中,session 物件是以 Map 方式被宣告!),我們要採用之前在內建的 OGNL Type Converter 中提到的 Map property 存取方式,所以就會是 #session['user'],取得 user 物件後我們就可以操作內部的 properties!
那我們還有哪些存取位置可以使用呢?下面就列出可用的位置:
  • parameters - 此 request 中的 parameter map
  • request - 如同 JSP 中的 request
  • session - 如同 JSP 中的 session
  • application - 如同 JSP 中的 application
  • attr - 根據 page, request, session 與 application 的順序查詢第一個找到的 data
  • ValueStack - 預設的存取位置
ValueStack
之前我們已經討論過很多關於 ValueStack 的資訊,現在我們要深入的討論 ValueStack。當 Strus2 framework 接收到使用者的 request 後,會先建立一個 ActionContext、ValueStack 與使用者呼叫的 action 物件,並且將 action 中的 properties 儲存到 ValueStack 中。這樣使用者就可以透過 OGNL 存取 ActionContext 中的 data。不過這裡有一點需要注意的是,在 ValueStack 中的 properties 只會出現最上層的 properties,也就是說如果有兩個 properties 是相同的變數名稱,只有最上層的 property 可以被存取。
你可能會好奇為什麼會有這樣情形發生呢?一個 class 中不可能有兩個變數是相同的名稱阿!沒錯!這的確有點弔詭,不過確實是有可能發生的,當我們的 Action 中的 Javabean property 採用 ModelDriven 的方式就會有可能:
public class ModelDrivenAction extends ActionSupport implements ModelDriven<User>
{
private User user = new User();
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Override
public User getModel()
{
return this.user;
}
@Override
public String execute() throws Exception
{
System.out.println("This.name = "+this.name+"\n User.name="+this.user.getName());
return SUCCESS;
}

}

上面的 ModelDrivenAction 中我們採用 ModelDriven 方式讓 user property 直接存取,然後我們在 execute method 中印出使用者在 textfield 中輸入的值會存在哪個 property 之下,回想一下我們如果使用 ModelDriven 方式,我們的 OGNL 就可以直接寫成:name 而不是 user.name,從 console 中可以發現,ModelDrivenAction 自己定義的 name property 被隱藏了!所以使用者輸入的值只會被除存在 user property 下的 name property!所以在 ValueStack 中會如下:
所以我在將物件作為 Action property 中建議採用 Object-backed Javabean property 方式,這樣可以減少日後維護的問題!

沒有留言: