tag:blogger.com,1999:blog-69906804032719334612024-02-22T01:10:45.639+08:00阿信 (Silver8250) 的冷泡咖啡館~努力不一定會成功,但不努力一定不會成功Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.comBlogger58125tag:blogger.com,1999:blog-6990680403271933461.post-22123044943529925442010-01-10T16:30:00.000+08:002010-01-10T16:30:41.231+08:00[Java] 小心 substring 的 memory leak這個內容並不是我自己發現的,剛好在 PTT 的 Java 版中看到某位高手所說得!其實,我是今天才知道有這回事!<br />
簡單的來說,假設我們有一段程式碼:<br />
<br />
<pre style="background: #ffffff; color: black;"><span style="color: #7f0055; font-weight: bold;">public</span> <span style="color: #7f0055; font-weight: bold;">class</span> SubstringMemoryLeak {
<span style="color: #7f0055; font-weight: bold;">private</span> <span style="color: #7f0055; font-weight: bold;">String</span> str = <span style="color: #7f0055; font-weight: bold;">new</span> <span style="color: #7f0055; font-weight: bold;">String</span>(<span style="color: #7f0055; font-weight: bold;">new</span> <span style="color: #7f0055; font-weight: bold;">byte</span>[10000]);
<span style="color: #7f0055; font-weight: bold;">public</span> <span style="color: #7f0055; font-weight: bold;">String</span> substring() {
<span style="color: #7f0055; font-weight: bold;">return</span> <span style="color: #7f0055; font-weight: bold;">this</span>.str.substring(0, 2);
}
<span style="color: #3f5fbf;">/**</span>
<span style="color: #3f5fbf;"> </span><span style="color: #7f9fbf; font-weight: bold;">*</span><span style="color: #3f5fbf;"> </span><span style="color: #7f9fbf; font-weight: bold;">@param</span><span style="color: #3f5fbf;"> args</span>
<span style="color: #3f5fbf;"> */</span>
<span style="color: #7f0055; font-weight: bold;">public</span> <span style="color: #7f0055; font-weight: bold;">static</span> <span style="color: #7f0055; font-weight: bold;">void</span> main(<span style="color: #7f0055; font-weight: bold;">String</span>[] args) {
<span style="color: #7f0055; font-weight: bold;">List</span><<span style="color: #7f0055; font-weight: bold;">String</span>> substringList = <span style="color: #7f0055; font-weight: bold;">new</span> ArrayList<<span style="color: #7f0055; font-weight: bold;">String</span>>(10000);
<span style="color: #7f0055; font-weight: bold;">for</span> (<span style="color: #7f0055; font-weight: bold;">int</span> i=0, n=10000; i<n; i++) {
substringList.add(<span style="color: #7f0055; font-weight: bold;">new</span> SubstringMemoryLeak().substring());
}
}
}
</pre><br />
如果執行上面的程式碼,你就會出現 Exception:<br />
<span style="background-color: white;">Exception in thread "main" java.lang.OutOfMemoryError: Java heap space</span><br />
<span style="background-color: white;"> at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:133)</span><br />
<span style="background-color: white;"> at java.lang.StringCoding.decode(StringCoding.java:173)</span><br />
<span style="background-color: white;"> at java.lang.StringCoding.decode(StringCoding.java:185)</span><br />
<span style="background-color: white;"> at java.lang.String.</span><init><span style="background-color: white;">(String.java:570)<br />
at java.lang.String.</span><init><span style="background-color: white;">(String.java:593)<br />
at silver8250.tools.SubstringMemoryLeak.</span><init><span style="background-color: white;">(SubstringMemoryLeak.java:8)<br />
at silver8250.tools.SubstringMemoryLeak.main(SubstringMemoryLeak.java:19)</span></init></init></init><br />
<init><init><init><br />
但是如果將 substring() method 修改成:<br />
</init></init></init><br />
<br />
<pre style="background: #ffffff; color: black;"><span style="color: #7f0055; font-weight: bold;">public</span> String substring() {
<span style="color: #7f0055; font-weight: bold;">return</span> <span style="color: #7f0055; font-weight: bold;">new</span> <span style="color: #7f0055; font-weight: bold;">String</span>(<span style="color: #7f0055; font-weight: bold;">this</span>.str.substring(0, 2));
}
</pre>神奇的事情發生了!程式竟然可以如願的執行!問題出現在哪?上面的程式碼最主要是建立一堆很佔記憶體的 String 物件,然後取其中的一小段!重點就在於 String 的 substring() method 的實做方式。<br />
首先,String 物件在記憶體中會以 char array 方式呈現,當我們每次建立一個 String 時,記憶體就會長出一塊 char array 來存放。String class 有一個 non-public constructor:<br />
<br />
String (int offset, int count, char[] value)<br />
<br />
當我們使用 substring 時,實際是用這個 constructor 來完成,也就是原先建立很佔記憶體的 String 物件並不會縮小,而是保持原來的 char array 所佔的大小。所以 Java 的 GC 就無法對此 char array 進行回收的動作。所以使用 substring() method 對字串內容來說,我們看到的是部份的,但是在記憶體中卻是佔有原先的大小!<br />
<br />
後來,我們改用 new String() constructor 來包裝 substring 的內容,這就會讓 Java 重新建一個 char array 來放置 substring 的內容,相對的,所佔得記憶體就小了,而且原先較大的 char array 就沒有任何 reference ,所以 GC 就可以直接回收了!<br />
<br />
老實說,這種情況可能不太常見,至少對我來說啦!我對於 String 的建立都會盡量改用 StringBuffer 物件,這樣不僅可以提供較好得效能,對於記憶體的利用也比較不會有問題!<br />
<br />
與大家分享之~Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-71316313894418064182009-11-21T16:40:00.000+08:002009-11-21T16:40:54.827+08:00[Eclipse] 好用的 Task List哇~好久沒有寫文章了!最近實在太忙碌了!不過忙碌之中也發現一些好用的技巧可以提供工作的效率喔!這也許你已經知道了,但容許我在說一次!<br />
<br />
正式開始工作後才發現,使用者需求如雪片般飛來,在加上手邊又有很多系統要弄,一堆事情夾雜在一起就會發現,修了這個系統卻忘了另一個系統要改哪些,導致工作效率就下降了~荷包就跟著縮水,年終相對減少,經濟壓力就變大....扯太多了!<br />
<br />
有太多工作要作,我們可以用傳統的紙筆來紀錄,那...紙髒了、掉了、濕了、字看不清楚了...工作效率就下降了~...進入一種無窮迴圈!接下來提供的方法也許不是最好,不過對我來說卻很方便 - Task List,先說好以下的前提是:針對程式碼的部份~<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF4AxeEYf_1kIP5GUi-ULECkJfgc5mPlSHD_CI5KoMcGabrAuvwZmhyphenhyphenvf9PPIxcwMs3tIVejFW-RS_hTWGbQ9TLkQUiavrbkqNjAKFPu5Sk97K2Epmu_UctRFCgAv30wNebklpl0BgvE8/s1600/1.bmp" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF4AxeEYf_1kIP5GUi-ULECkJfgc5mPlSHD_CI5KoMcGabrAuvwZmhyphenhyphenvf9PPIxcwMs3tIVejFW-RS_hTWGbQ9TLkQUiavrbkqNjAKFPu5Sk97K2Epmu_UctRFCgAv30wNebklpl0BgvE8/s320/1.bmp" /></a>當我們使用 Eclipse 編輯程式時,我們可以寫註解(廢話~),不過有一些註解對於 Eclipse 來說是有特別處理的,例如:TODO、FIXME 等。當我要開始修改或開發某些程式片段前,我都會先註記 TODO,也就 class 之前或 method 之前加上 <span style="color: red;">//TODO</span> ,當某個程式我現在沒時間思考問題點在哪,但是就是有問題時,我就會註記 <span style="color: red;">//FIXME</span> ,這樣的紀錄 Eclipse 可以幫我們整理出來 workspace 中所有 project 的註記,這樣我們就可以很快的回想自己的工作清單,從 Window -> Show View -> Others 來開啟 Tasks View,如右圖:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgShQJdenC7G06ozDHrHgaaImakgex3ZcUGn8K-DJjHkx8c2wnp2OlsEbnahtjeHH8b4kNnNdqaDnslaMBod48VVrmSzX_4dTFblTgsoAITxi_n58wH8TLWIVVMvinJH_N0ot-C1xCDAJs/s1600/1.bmp" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgShQJdenC7G06ozDHrHgaaImakgex3ZcUGn8K-DJjHkx8c2wnp2OlsEbnahtjeHH8b4kNnNdqaDnslaMBod48VVrmSzX_4dTFblTgsoAITxi_n58wH8TLWIVVMvinJH_N0ot-C1xCDAJs/s320/1.bmp" /></a><br />
</div>從 Tasks View 中我們可以看到一堆已經被註記的工作清單,如左圖。直接對 task 點兩下就可以直接開啟對應的程式碼片段,這樣我們就可以很容易的紀錄我們之前是作到哪了!<br />
<br />
這工具對我來說挺不錯用的!尤其在工作越來越多的時候更是一個好得工作利器!與大家分享之~Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com2tag:blogger.com,1999:blog-6990680403271933461.post-49319488989703881682009-10-11T10:11:00.001+08:002009-10-11T14:41:36.228+08:00[Struts2] Other UI Tags在前兩篇的文章中,我們提到了許多的 UI tags,包含<a href="http://silver8250.blogspot.com/2009/07/struts2-simple-ui-tags.html">基本的</a>與 <a href="http://silver8250.blogspot.com/2009/07/struts2-collection-based-ui-tags.html">Collection-based</a>,在這裡我將一些比較不屬於前兩種,但是也是有用的 tags 加以介紹,這應該算是 bonus!以下將介紹三種 tags:1) Label tag、2) Hidden tag 與 3) Double Select tag。<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">1) Label Tag</span></span><br />Label tag 主要是用來顯示某項資料,並且讓顯示的結果與其他的 UI tags 有相同的格式,也就是以同樣的 theme 來顯示,雖然我們可使用 property tag 來幫助我們顯示,不過這樣就無法與其他 UI tags 使用相同的 theme 了!<br />在使用 Label tag 時,我們只需要設定 name 與 label attributes 就可以簡單的將 data 帶出!不過因為 Label tag 只是顯示資料,所以資料只會被帶出,不會被帶入!以下就是簡單的例子:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">label</span> name=<span style="color: rgb(42, 0, 255);">"user.name"</span> label=<span style="color: rgb(42, 0, 255);">"Your Name"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />由於 Label tag 的使用是非常簡單的,我們就不再多說了!<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">2) Hidden Tag</span></span><br />Hidden tag 的使用也很簡單,此 tag 最主要是希望將某些資料被 form 送出但不要讓使用者知道,在某些情況下是很好用的,例如我們希望使用者填寫 form 的時間不要超過 10 分鐘,我們可以使用 Hidden tag 來紀錄時間,當使用者送出該表單時,後端在將目前時間與 Hidden tag 所送來的時間進行判斷。<br />由於 Hidden tag 使用方式容易,以下就舉一個例子示範:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:hidden</span> name=<span style="color: rgb(42, 0, 255);">"timestamp"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><span style="font-weight: bold;font-size:130%;" ><br />3) Double Select Tag</span><br />Double Select Tag 算是一個很神奇的 UI tag,因為他提供了一種很特別的功能。讓我們想像一個情況,當我們在註冊某個網站時,我們應該常常會遇到要填寫住址,比較好得網站會給我們兩個下拉選單,一個選擇縣市,另一個則是顯示該縣市對應的鄉鎮市區!對於 programmers 來說,要達到這樣的功能,不外乎採用的是 Javascript 的技術,不過是 synchronize 或 asynchronize,我們都要依靠 Javascript 的幫助,但是有時候我們又不擅長撰寫 Javascript,所以 Double Select tag 就因應而生。<br />Double Select tag 提供了兩個下拉選單,第一個選擇後,第二個選單會根據第一個選單的選擇來產生第二個選單的內容,就如同我在上面所描述的情況一樣!對於 Double Select tag 來說,list, listKey 與 listValue attributes 就相同於 Select tag 的使用,只是 Double Select tag 多了以下的一些 attributes:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><tbody><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /><tr><br /><td>doubleName</td><br /><td>String</td><br /><td>儲存第二個下拉選單的選擇值</td><br /></tr><br /><tr><br /><td>doubleList</td><br /><td>Collection-based</td><br /><td>第二個下拉選單</td><br /></tr><br /><tr><br /><td>doubleListKey</td><br /><td>String</td><br /><td>第二個下拉選單所產生 option 的 key</td><br /></tr><br /><tr><br /><td>doubleListValue</td><br /><td>String</td><br /><td>第二個下拉選單所產生 option 的顯示</td><br /></tr><br /></tbody></table></div><br />除了原先在 Select tag 中我們就使用過得 list, listKey 與 listValue 之外,所有想要設定 Double Select tag 的第二個下拉選單,attributes 的開頭都是以 double 為首。上面中比較重要的是 doubleName attribute,因為他是必填的選項,doubleName attribute 的目的在於儲存使用者選擇第二個下拉選單的內容,就如同 name attribute 是儲存第一個下拉選單的結果一樣。<br />那我們要如何讓第二個下拉選單可以動態呢?有兩種方法,第一種是將第二個下拉選單,透過 OGNL 方式寫在 double select tag 中:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:doubleSelect</span> name=<span style="color: rgb(42, 0, 255);">"first"</span><br /> list=<span style="color: rgb(42, 0, 255);">"beanList"</span><br /> listKey=<span style="color: rgb(42, 0, 255);">"beanId"</span> listValue=<span style="color: rgb(42, 0, 255);">"beanValue"</span><br /><br /> doubleName=<span style="color: rgb(42, 0, 255);">"second"</span><br /> doubleList=<span style="color: rgb(42, 0, 255);">"%{top.beanId==1?{'1.1','1.2'}:{'2.1','2.2'}}"</span><br /><span style="color: rgb(127, 0, 85);">/></span><br /></pre>這種方式就必須將第二個下拉選單的內容寫死在頁面上,如果我們需要增加不同的內容,就會顯得礙手礙腳!除非內容很單純,也不會有太多的變化,否則我是不太建議使用這樣的方式來作。第二種方式就顯得有彈性了,我們將第二個下拉選單到 Action 中取得,剩下的就交由 Struts2 framework 幫我們用 javascript 來自動切換:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:doubleSelect</span> name=<span style="color: rgb(42, 0, 255);">"first"</span><br /> list=<span style="color: rgb(42, 0, 255);">"beanList"</span><br /> listKey=<span style="color: rgb(42, 0, 255);">"beanId"</span> listValue=<span style="color: rgb(42, 0, 255);">"beanValue"</span><br /><br /> doubleName=<span style="color: rgb(42, 0, 255);">"second"</span><br /> doubleList=<span style="color: rgb(42, 0, 255);">"nextList(top.beanId)"</span><br /> doubleListKey=<span style="color: rgb(42, 0, 255);">"nextId"</span><br /> doubleListValue=<span style="color: rgb(42, 0, 255);">"nextValue"</span><br /><span style="color: rgb(127, 0, 85);">/></span><br /></pre>這種方式就代表我們的 Action 中有一個 nextList() method,接受 beanId 的資料型態。也就是如果 beanId 是一個 String,則此 method 就會是 nextList(String next)。當然,我們在這裡回傳的第二個下拉選單內容是以一個 List 來包裝一堆的 JavaBean。透過 nextList,我們可以根據不同的 parameter 內容來回應不一樣的下拉選單內容,達到動態的效果。<br />如果你仔細觀察就會發現,double select 看似有 AJAX 的動態取內容的效果,其實卻不然,當 double select tag 被執行時,framework 會先迭代 list (也就是第一個下拉選單的內容),並且逐一取得所有的第二個下拉選單內容,再將這些內容組織到 javascript 中!也就是如果第一個下拉選單中有 10 個項目,那我們的 nextList() method 就會先被呼叫 10 次!<br />另外,讓我們回到上面的範例中,你應該有發現 OGNL 中的 top 關鍵字吧!這是用來儲存第一個下拉選單所選到的物件,就如同我們的範例,我們每一個物件都是一個 JavaBean,所以我們就可以用 OGNL 的特性,將 bean 中的 property 傳遞給 nextList() method!<br /><br />在這裡我介紹了三個很特殊,但不一定常用到的 tags,Struts2 framework 提供了很多種神奇的 tags,讓 programmer 可以很容易的撰寫出複雜的表單畫面,不過關方網站的說明實在是很少,所以要使用這些 tags 的代價就是必須自己先 survey 過!<br /><br />P.S. 這篇內容原本要在 8/11 完成,沒想要拖了整整 2 個月,中間歷經了當兵,現在又忙於工作,閒暇之餘趕緊完成~希望大家多多指教~Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-28289344854808127582009-07-29T11:23:00.010+08:002009-07-29T15:20:29.708+08:00[Struts2] Collection-based UI Tags除了之前比較<a href="http://silver8250.blogspot.com/2009/07/struts2-simple-ui-tags.html">簡單的 UI tags</a> 之外,Struts2 framework 當然也會提供一些比較複雜的 tags,不然就無法符合使用者的需求了!在這裡我們將會介紹三種以 Collection 為基礎的 UI tags:1) select tag、2) radio tag 與 3) checkbox list tag。<br /><br />這裡所謂的 Collection-based 就是在 Java 端的 property 是以 Collection 為主,如 List、Map 等,而這些 property 要如何在頁面上顯示,就是這裡的重點了!同樣的,在這裡我只會介紹每個 tag 特殊的 attributes,而共用的 attributes 就請參考 <a href="http://silver8250.blogspot.com/2009/07/struts2-simple-ui-tags.html">Simple UI Tags</a> 的共同 attributes。<br /><br /><span style="font-weight: bold;font-size:130%;" >1) Select Tag</span><br />Select Tag 對於 Collection-based UI Tags 來說,應該是最常使用到的 tag!因為下拉式選單是最常見到的顯示方式,下面是最簡單的範例:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">select</span> name=<span style="color: rgb(42, 0, 255);">"user.name"</span> list=<span style="color: rgb(42, 0, 255);">"{'Silver','Brian','Mike'}"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />我們使用到 OGNL 的 List 型態,並且由 OGNL 幫我們快速建立。name attribute 就是該選單選到的值要被儲存到的目標 property,而 list 則是我們要顯示在頁面的 Collection-based 物件。以下就是 select tag 的 attributes:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><tbody><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /><tr><br /><td>list</td><br /><td>Collection-based type, List, Map, Array or Iterator</td><br /><td>用來產生 select tag 的 option list</td><br /></tr><br /><tr><br /><td>listKey</td><br /><td>String</td><br /><td>根據 list attribute 中,指定 property 用來產生 option tag 的 key</td><br /></tr><br /><tr><br /><td>listValue</td><br /><td>String</td><br /><td>根據 list attribute 中,指定 property 用來產生 option tag 的 value</td><br /></tr><br /><tr><br /><td>headerKey</td><br /><td>String</td><br /><td>類似於 listKey,產生在 Collection 之前</td><br /></tr><br /><tr><br /><td>headerValue</td><br /><td>String</td><br /><td>類似於 listValue,產生在 Collection 之前</td><br /></tr><br /><tr><br /><td>emptyOption</td><br /><td>Boolean</td><br /><td>指定是否自動產生空的 option 在 header 之前</td><br /></tr><br /><tr><br /><td>multiple</td><br /><td>Boolean</td><br /><td>是否可以多重選擇</td><br /></tr><br /><tr><br /><td>size</td><br /><td>Integer</td><br /><td>指定一次顯示的筆數,會有 scroll bar 輔助多餘的 option</td><br /></tr><br /></tbody></table><br /></div><br />上述的 attributes 中,比較需要注意的是 list, listKey 與 listValue 這三個,list 是用來設定我們要顯示的 Collection-based property,在這裡要注意的是 list attribute 的型態只能接受 Collection, Array, List, Map 與 Iterator,如果型態不對就會出現錯誤!另外,listKey 則是根據 list 中所存放物件的某個 property,例如:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> List<UserBean> userList;<br /></pre><br />userList 中存放了一堆的 UserBean 物件,每個 UserBean 物件都有 name 與 age 的 property,那我們的 select tag 撰寫成:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">select</span> list=<span style="color: rgb(42, 0, 255);">"userList"</span> listKey=<span style="color: rgb(42, 0, 255);">"age"</span> listValue=<span style="color: rgb(42, 0, 255);">"name"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />就是指定每一個 select 之下的 option 都會以 UserBean 物件中的 age 作為 key (也就是作為 option tag 中的 value attribute),而 name property 則為 option 顯示的內容。也就會變成如下:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">select</span><span style="color: rgb(127, 0, 85);">></span><br /><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">option</span> value=<span style="color: rgb(42, 0, 255);">"10"</span><span style="color: rgb(127, 0, 85);">></span>Silver<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">option</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">option</span> value=<span style="color: rgb(42, 0, 255);">"24"</span><span style="color: rgb(127, 0, 85);">></span>Su<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">option</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">option</span> value=<span style="color: rgb(42, 0, 255);">"50"</span><span style="color: rgb(127, 0, 85);">></span>Jason<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">option</span><span style="color: rgb(127, 0, 85);">></span><br /><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">select</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />從上面的結果我們也可以知道,我們的 userList 中存有三個 UserBean 物件。<br /><br />而比較特別的是 headerKey 與 headerValue 這兩個 attributes,就目前為止我也搞不清楚這兩個 attributes 有什麼特別的用處,如果我們指定的 list 物件中需要增加一項資料,但我們不想動到 list 物件,那我們可以使用 headerKey 與 headerValue 來增加一項 option。而 emptyOption attribute 則是指定我們是否需要 Struts2 framework 幫我們自動產生一個空白的 option。size attribute 則是用來指定一次顯示的數量,如果我們不指定就會是一次顯示一項,這樣會比較像是下拉式選單,如果顯示數量大於 1 又小於 list 的總數量,則會多了上下的 scroll bar 幫助我們選擇。<br /><br />這幾個 attribute 中比較有趣的是 multiple,以往的下拉式選單都是多選一,如果我們指定 multiply="true",選單就可以呈現多選,在不指定 size attribute 之下,多選的選單就會將整個 list 展開。如果我們採用多選的選單,選單送出的 property (也就是 name attribute 所只到的 property) 必須是一個 Collection-based property,這點應該很直觀,既然可以多選,就表示我們需要用 Collection-based 物件來接收,否則就失去多選的意義了!<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">2) Radio Tag</span></span><br />使用 radio tag 就如同使用 select tag 一樣,只是少了許多 attributes 可以使用罷了:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><tbody><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /><tr><br /><td>list</td><br /><td>Collection-based type, List, Map, Array or Iterator</td><br /><td>用來產生 select tag 的 option list</td><br /></tr><br /><tr><br /><td>listKey</td><br /><td>String</td><br /><td>根據 list attribute 中,指定 property 用來產生 radio 的 key</td><br /></tr><br /><tr><br /><td>listValue</td><br /><td>String</td><br /><td>根據 list attribute 中,指定 property 用來產生 radio 的 value</td><br /></tr><br /></tbody></table><br /></div><br />由於使用的方式大同小異,所以我就不舉例了!而要注意的是,radio tag 一定是單選,所以 name attribute 指到的 property 可以是 primitive type 或 Collection-based type。<br /><br />3) Checkbox List Tag<br />使用 checkboxlist tag 跟 select 也是類似,只是要注意的是,不要將 checkbox 與 checkboxlist 搞混了!checkbox 是單選,checkboxlist 是多選!而 checkboxlist 提供的 attributes 有:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><tbody><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /><tr><br /><td>list</td><br /><td>Collection-based type, List, Map, Array or Iterator</td><br /><td>用來產生 select tag 的 option list</td><br /></tr><br /><tr><br /><td>listKey</td><br /><td>String</td><br /><td>根據 list attribute 中,指定 property 用來產生 checkbox 的 key</td><br /></tr><br /><tr><br /><td>listValue</td><br /><td>String</td><br /><td>根據 list attribute 中,指定 property 用來產生 checkbox 的 value</td><br /></tr><br /></tbody></table><br /></div><br />不過在這裡要注意的是,因為 checkboxlist 一定是多選,所以 name attribute 指到的 property 一定是要 Collection-based type。<br /><br />最後我想探討的是如何將 list 的選項初始化,由於 name attribute 是指定該元件在表單送出後的目的地,如果我們要讓某個 select tag 中的 option 被預先選起來,我們就要在相對應的 property 事先給予值,例如我們有一個 select tag 如下:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">select</span> name=<span style="color: rgb(42, 0, 255);">"myFavorite"</span> list=<span style="color: rgb(42, 0, 255);">"favoriteList"</span><br /> listKey=<span style="color: rgb(42, 0, 255);">"key"</span> listValue=<span style="color: rgb(42, 0, 255);">"value"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre>這個 select tag 的目的地是 myFavorite property,如果我們希望 list 中某個 option 先被選起來,我們就將 favoriteList 物件中的該項目的 key 填入 myFavorite property,這樣 Struts2 framework 就會幫我們選起該 option 了!總之記得,資料怎麼來就怎麼去!Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-7010267576280565852009-07-16T16:21:00.001+08:002009-07-16T23:10:33.439+08:00[Struts2] Simple UI Tags之前我們介紹了一些 tags,主要都是輔助 programmers 完成頁面上資料的存取,在這裡我將開始描述關於顯示在頁面上與使用者互動的表單 (form)。不過,我先簡介一些共同有的 attributes,因為這些 attributes 如果在介紹 tag 時重複敘述,感覺過於累贅!<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">共同 attributes</span></span><br /><br />共同的 attributes 就是所有的 tags 都會擁有的,為了之後將重點放在各個 tags 上,我們就將這些 attributes 分開來介紹,這裡要注意的是,如果某個 attribute 的 type 屬於 String,那就表示該 attribute 的 value 只能接受字串,如果想要將 OGNL expression language 寫入,就必須在 expression 加上 %{} 符號 - %{ expression } - 這樣 Struts2 framework 就可以正確解析 OGNL 了!如果該 type 屬於 Object,那你就可以大方的將 OGNL expression 置入,無須加上 %{} 符號了!<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><thead><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /></thead><br /><tbody><br /><tr><br /><td>name</td><br /><td>String</td><br /><td>用來設定 form 元件,並且會依照 name 所設定的值,將其對應到 Action 中的 property</td><br /></tr><br /><tr><br /><td>value</td><br /><td>Object</td><br /><td>用來設定 form 元件中所包含的值</td><br /></tr><br /><tr><br /><td>key</td><br /><td>String</td><br /><td>i18n 用</td><br /></tr><br /><tr><br /><td>label</td><br /><td>String</td><br /><td>設定某個 form 元件顯示的字串</td><br /></tr><br /><tr><br /><td>labelposition</td><br /><td>String</td><br /><td>設定 label 顯示的位置:left 或 top</td><br /></tr><br /><tr><br /><td>required</td><br /><td>Boolean</td><br /><td>如果設定為 true,則該 form 元件的 label 會出現 * 符號</td><br /></tr><br /><tr><br /><td>id</td><br /><td>String</td><br /><td>相同於 HTML 的 id 屬性,通常用於 java script 與 CSS</td><br /></tr><br /><tr><br /><td>cssClass</td><br /><td>String</td><br /><td>相同於 HTML 的 css 屬性</td><br /></tr><br /><tr><br /><td>cssStyle</td><br /><td>String</td><br /><td>相同於 HTML 的 style 屬性</td><br /></tr><br /><tr><br /><td>disabled</td><br /><td>Boolean</td><br /><td>相同於 HTML 的 disabled 屬性</td><br /></tr><br /><tr><br /><td>tabindex</td><br /><td>String</td><br /><td>相同於 HTML 的 tabindex 屬性</td><br /></tr><br /></tbody><tfoot></tfoot></table><br /></div><span style="font-size:130%;"> <span style="font-weight: bold;"><br />Simple UI tags</span></span><br /><br />上面描述的是共同的 attribute,以下將介紹一些 UI 相關並基礎的 tags,對於共同的 attribute 就不會重複提及。<br /><br /><span style="font-weight: bold;">1) Head Tag</span><br />對於任何的 HTML 或 JSP 來說,要產生一個網頁通常都是以 <head> 開頭,對於 Struts2 提供的 <head> 來說,卻不同於一般的 HTML head tag,如果你試過 Struts2 提供的 head tag,你應該會發現,tag 本身並不作任何事情,而是在網頁中默默的幫我們加入一些檔案連結,這些檔案的連結目的在於支援其他的 tag 完成設定。在使用 Struts2 提供的 head tag 時,一定要放置於 HTML 的 head tag 之下,否則自動產生的檔案連結會放到不正確的位置而導致無法使用某些功能!<br />Struts 的 head tag 本身並沒有其他的 attribute 需要設定,我們可以簡單的使用:<br /><br /><s:head /><br /><br />這樣的撰寫就會自動幫我們產生一些檔案的連結。<br /><br /><span style="font-weight: bold;">2) Form Tag</span><br />Form tag 對於 UI 來說是最重要的 tag,因為他是所有 UI tag 的上層元件,如果對於 HTML 熟悉的話,form tag 的使用應該不會陌生,而且我們之前就看過蠻多的範例了!下表是一些 form tag 的 attributes (除了共同 attributes 之外):<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><thead><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /></thead><br /><tbody><br /><tr><br /><td>action</td><br /><td>String</td><br /><td>指定該 form 送出的目標 Action</td><br /></tr><br /><tr><br /><td>namespace</td><br /><td>String</td><br /><td>目標 Action 的 namespace,預設值是目前的 namespace</td><br /></tr><br /><tr><br /><td>method</td><br /><td>String</td><br /><td>相同於 HTML 的 method attribute,預設值是 POST</td><br /></tr><br /><tr><br /><td>target</td><br /><td>String</td><br /><td>相同於 HTML 的 target attribute</td><br /></tr><br /><tr><br /><td>enctype</td><br /><td>String</td><br /><td>若要使用檔案上傳,則填入 multipart/form-data</td><br /></tr><br /><tr><br /><td>validate</td><br /><td>Boolean</td><br /><td>設定該 form 是否使用 javascript 驗證,當使用 Validation Framework 時,此 attribute 要設定為 true,驗證功能才能正常運作</td><br /></tr><br /></tbody><br /></table></div><br />在上述眾多 attributes 中,最重要的莫過於 action 了!因為他主導整個 form 的目標 action,在這裡設定 action attribute 時要注意,我們無須填入 .action 的縮寫,因為 form 元件會幫我們完成,如果目標的 action 與現在是相同的 namespace,我們就無須填寫 namespace,否則我們就需要 namespace attribute 幫助我們找到 Action。每當 form 元件被執行時會有以下三種可能發生的狀況:<br /><ol><li>如果 action attribute 沒有被設定,則 form 的目標就會指向目前的 Action</li><li>如果有指定 action attribute,form 就會根據 Action 的位置配合上 namespace 來設定目標。如果 namespace 沒有被指定,就會使用目前的 namespace。當我們在設定 action attribute 時,我們無須加上 .action 的延伸網址<br /></li><li>如果 action attribute 所指定的值不屬於一個 Action,form 元件的目標就會直接將該值作為目標。這種情況下,我們就需要自己加上延伸網址 (如:.jsp, .html 等),並且我們的相對位址需要以 / 作為開頭,例如:/myApp/b.jsp。另外要注意的是,如果我們採用此種方法,即使我們指定 namespace attribute,form 元件也會自動忽略!<br /></li></ol><span style="font-weight: bold;">3) Textfield Tag</span><br />這個 tag 對於你來說也是無可避免的!因為沒有此 tag,你就無法取得使用者的資料,所以這個 tag 也算是最常使用的!不過要注意的是 textfield tag 的 name 與 value attribute,一般來說,我們無需要指定 value attribute,因為我們只需要指定 name attribute 讓 OGNL 幫我們去 ActionContext 中取得對應的值來填入,當然,我們也可以填寫 value attribute,但是要注意的是,如果我們自己有填寫 value attribute,那 name 指定的 OGNL 所取到的值就不會顯示在 textfield tag 上了,這點要注意喔!以下是一些 textfield tag 的 attributes:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><thead><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /></thead><br /><tbody><br /><tr><br /><td>maxlength</td><br /><td>String</td><br /><td>指定 textfield 的可輸入最大長度</td><br /></tr><br /><tr><br /><td>readonly</td><br /><td>Boolean</td><br /><td>如果設定為 true,則該 textfield 就無法輸入字串</td><br /></tr><br /><tr><br /><td>size</td><br /><td>String</td><br /><td>textfield 的長度</td><br /></tr><br /></tbody><br /></table></div><br /><span style="font-weight: bold;">4) Password Tag</span><br />這個 tag 跟 textfield 幾乎是一樣的,不過當使用者輸入字串時,所有的文字都會被包裝成其他的符號,以防資料外洩!這個 tag 的使用其實跟 textfield 是相同的,所以我就不多說了!以下是 password tag 的 attributes:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><thead><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /></thead><br /><tbody><br /><tr><br /><td>maxlength</td><br /><td>String</td><br /><td>指定 textfield 的可輸入最大長度</td><br /></tr><br /><tr><br /><td>readonly</td><br /><td>Boolean</td><br /><td>如果設定為 true,則該 textfield 就無法輸入字串</td><br /></tr><br /><tr><br /><td>size</td><br /><td>String</td><br /><td>textfield 的長度</td><br /></tr><br /><tr><br /><td>showPassword</td><br /><td>Bololean</td><br /><td>使否將 OGNL 取到的值顯示在 value 中,預設 false</td><br /></tr><br /></tbody><br /></table></div><br />上面列出的 attributes 中,幾乎跟 textfield 是一樣的,不過多了一個 showPassword attribute,這個 attribute 是讓我們設定是否要將 name attribute 中指定的 OGNL 取到的值顯示,在這裡我所謂的顯示不是大剌剌的用文字方式顯示,當然是有被遮蔽的顯示,不過從原始碼中還是可以看到原來的值,所以 showPassword attribute 預設值為 false,為的就是不要產生一些 security issues 來困擾 programmers,在這裡我也建議你不要將此 attribute 設定為 true!<br /><br /><span style="font-weight: bold;">5) Textarea Tag</span><br />同樣的,textarea 從開發角度上來說,與 textfield 並沒有太大的差別,主要就是提供使用者多行的輸入欄位!以下就是一些 textarea tag 的 attributes:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><thead><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /></thead><br /><tbody><br /><tr><br /><td>cols</td><br /><td>Integer</td><br /><td>指定 textarea 的行數</td><br /></tr><br /><tr><br /><td>rows</td><br /><td>Integer</td><br /><td>指定 textarea 的列數</td><br /></tr><br /><tr><br /><td>readonly</td><br /><td>Boolean</td><br /><td>如果設定為 true,則該 textarea 就無法輸入字串</td><br /></tr><br /><tr><br /><td>wrap</td><br /><td>String</td><br /><td>相同於 HTML 的 wrap attribute</td><br /></tr><br /></tbody><br /></table></div><br /><span style="font-weight: bold;">6) Checkbox Tag</span><br />這裡的 checkbox 元件如果你認為跟 HTML 的 checkbox 一樣的話,那你就錯了!因為在 Struts2 提供的 checkbox 元件是一個單一 HTML 的 checkbox,這是什麼意思呢?一般的 HTML checkbox 提供了多個選項讓使用者選擇零或多個,但是這裡的 checkbox 只提供使用者一個選項,也就是選或不選。所以這個 checkbox 不同於 HTML 的 checkbox。這個 checkbox 只提供 Boolean 的功能,也就是說其對應的 property 一定要是一個 boolean property,如果要像 HTML 提供的 checkbox 有相同功能的話,要改使用 checkboxlist tag,現在不會提到!<br />以下是 checkbox tag 的 attribute:<br /><style type="text/css">.nobrtable br{display:none}</style><br /><div class="nobrtable"><br /><table border="1"><br /><thead><tr><br /><th>Attribute</th><br /><th>Type</th><br /><th>Description</th><br /></tr><br /></thead><br /><tbody><br /><tr><br /><td>fieldValue</td><br /><td>String</td><br /><td>這裡所填寫的 value 是真正會被傳送到 Action 中的值,主要是 true 或 false,預設值為 true</td><br /></tr><br /><tr><br /><td>value</td><br /><td>String</td><br /><td>這裡是用來判斷此 checkbox 是否會被勾選</td><br /></tr><br /></tbody><br /></table></div><br />看到上面的 attribute 你一定會感到很奇怪,value attribute 已經是共同的 attribute 了,為何在這裡還會在重新點出呢?原因就在於 checkbox 元件還有一個 fieldValue 這會與 value attribute 搞混!所以在這裡要特別點名。<br />fieldValue attribute 中的值代表當此 form 被送出後,fieldValue 中填寫的值會真正被送到 Action 中對應的 property,而 value attribute 只是用來判斷此 checkbox 是否需要被勾選。當 OGNL 取得 ValueStack 中的值,會將此值寫入 value attribute 中 (任何一個元件都是這樣),使用者可以看到此 checkbox 是否有被選起來。所以 fieldValue 與 value attribute 之間不要弄混了!<br />另外,checkbox 所指定的 name attribute,其對應到的 property 需要是一個 boolean 型態,否則會無法運作!<br /><br />在這裡列出了六種簡單的 UI tag 給你參考,這裡列出的都是針對單一的 property,之後我將介紹關於 Collection-based 相關的 UI tags。Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-13585959152476078242009-07-08T15:53:00.002+08:002009-07-31T10:09:17.519+08:00[Struts2] 探討 OGNL 運算功能關於 OGNL (Object-Graph Navigation Language),我們之前已經提過相當的多了!不過那些都只是在 navigate 的部份,也就是如何將資料在 String-based HTTP 與 Java-type 之間做轉換與存取。熟悉 JSP 的 Expression Language (EL) 的 programmers 來說,EL 不僅僅提供了存取資料的功能,也提供好用的運算功能!身為比 EL 更強大的 OGNL 來說,這也是基本的配備!<br /><span style="font-size:130%;"><br /><span style="font-weight: bold;">OGNL and Collection</span></span><br />之前我們有提過 OGNL 如何在 Collection 型態的物件進行 navigate,但不只這樣喔~對於 Collection 型態的物件,OGNL 提供了一些好用的功能:1) 取得資訊、2) 快速建立 與 3) Filter and Project。<br /><br />1. 取得資訊<br />如果是要取得 Collection 中的資料,我在<a href="http://silver8250.blogspot.com/2009/04/struts2-ognl-type-converter.html">內建 OGNL Type Converter</a> 中就有提過,不過我在這裡想要說得是,如果我們想要知道 Collection 中的大小,那我們是不是要將 Collection 的大小先儲存到某個 property 中?答案當然是不用!OGNL 怎麼不會提供這樣的功能勒~不僅僅是 Collection 大小,就連判斷是否為 empty 都有支援。<br />如果我們要取得 Array 的大小,我們就要使用 array.length;如果是 list,則是 list.size;而 map 的話就與 list 是相同的。而 empty 的判斷只有 list 和 map 有提供,使用都是 list.isEmpty。<br /><br />2. 快速建立<br />在 JSP 頁面中所使用的 Collection 型態物件,不見得都要屬於 Action 的 property,OGNL 提供了快速建立 Collection 物件的機制,讓 programmers 可以容易的建立 list 或 map,這對於在頁面上提供一個 select 下拉式選單來說是很方便的。不過目前我不想提到關於 select 選單的部份,主要先考慮如何快速建立 list 或 map,對於下列的 Java code 你應該不陌生:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> List<String> createList() {<br /><span style="color: rgb(127, 0, 85); font-weight: bold;"> List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>> list = <span style="color: rgb(127, 0, 85); font-weight: bold;">new</span> ArrayList<<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>>();<br /> list.add(<span style="color: rgb(42, 0, 255);">"Hello"</span>);<br /> list.add(<span style="color: rgb(42, 0, 255);">"Silver"</span>);<br /> list.add(<span style="color: rgb(42, 0, 255);">"Welcome"</span>);<br /><span style="color: rgb(127, 0, 85); font-weight: bold;"> return</span> list;<br />}<br /></pre>這段程式碼主要就是建立一個 list 並且容納我們想要的字串物件,對於 OGNL 來說,要建立一個一樣的 list,簡直是易如反掌:<br /><br /><span style="color: rgb(51, 51, 255);">{"Hello", "Silver", "Welcome"}</span><br /><br />別懷疑自己的眼睛,沒錯!就是這麼簡單~我們只要短短的 OGNL 就可以幫我們實現出一串的 Java code。現在的你是否也想要知道 map 的建立呢?別急,我們先看看一段程式碼:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> Map<Integer, String> createMap() {<br /> Map<<span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>, <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>> map = <span style="color: rgb(127, 0, 85); font-weight: bold;">new</span> HashMap<<span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>, <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>>();<br /> map.put(1,<span style="color: rgb(42, 0, 255);">"Hello"</span>);<br /> map.put(3,<span style="color: rgb(42, 0, 255);">"Silver"</span>);<br /> map.put(100,<span style="color: rgb(42, 0, 255);">"Welcome"</span>);<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> map;<br />}<br /></pre>這同樣也是你很熟悉的程式碼,我想我就直接告訴你這樣的 OGNL 會是:<br /><span style="color: rgb(51, 51, 255);">#{1: "Hello", 3: "Silver", 100:"Welcome"}</span><br /><br />看到這裡,連我自己都不禁懷疑,到底 OGNL 在哪裡需要 # 符號,在哪裡又不需要呢?目前我還看不透,不過自己在使用時就記住,list 不用 # 而 map 需要~<br /><br />3. Filter and Project<br />OGNL 除了上面兩種功能外,另外還提供了過濾 (filter) 的功能與投影 (Project),這看起來匪夷所思,實際上卻是很好用的東西。Filter 就是 OGNL 提供讓 programmers 針對 Collection 撰寫 rule 來取出我們想要的東西;Project 就是將某個 Collection 指取出部份的資料並投影到另一個 Collection 之中。這兩個東西乍看之下好像很類似,其實不然,直接舉個例子應該會比較清楚。假設我們有一個 User 物件:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> User {<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> name;<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> age;<br /><span style="color: rgb(63, 127, 89);">//getter and setter methods</span><br />}<br /></pre>然後,我們有一個 List 儲存一堆的 user 為 users,這次我們先展示 filter 功能,如果我們將 OGNL 撰寫成:<br /><span style="color: rgb(51, 51, 255);">users.{? #this.age > 18}</span><br /><br />則 OGNL 會幫我們將 users 中的所有 User 物件的 age 進行過濾,並且依照我們所指是的 rule 完成,而 rule 中的 #this 代表每一次 OGNL 迭代所指向的目標物件 (類似於 iterator.next())。<br />接下來是 project 的部份,同樣的我們使用 users 來示範,如果我們的 OGNL 為:<br /><span style="color: rgb(51, 51, 255);">users.{name+' '+age}</span><br /><br />則 OGNL 會幫我們將 users 中的所有 User 物件的 name 與 age 取出,並用一個空白字串組成新的字串,在存成一個新的 list 之中。當然,我們可以結合 filter 與 project:<br /><br /><span style="color: rgb(51, 51, 255);">users.{? #this.age > 18}.{name+' '+age}</span><br /><br />這個結果就是上面兩個 OGNL 的結合。不知道你是否有注意到一個規則,當我們在使用 filter 時,我們的撰寫方式為:<br /><br /><span style="color: rgb(51, 204, 0);">collectionName.{? expression }</span><br /><br />反之,當我們使用 project 時,我們的 OGNL 撰寫為:<br /><br /><span style="color: rgb(51, 204, 0);">collectionName.{ expression }</span><br /><br />也就是 filter 的 expression 之前有加 ? 符號,而 project 沒有。<br /><span style="font-weight: bold;font-size:130%;" ><br />Other Functions</span><br />OGNL 不僅提供上述的功能,另外提供對於運算方面的功能以及一些特殊的功能:1) 運算功能、2) 呼叫 method 與 3) 存取 static method 與 field。<br /><br />1) 運算功能<br />OGNL 提供了多種型態的變數讓 programmers 可以快速的宣告:<br /><ul><li>char - 'a'</li><li>String - "a" 或 'silver'<br /></li><li>boolean - True, False</li><li>int - 123</li><li>BigDecimal - 123b</li><li>BigInteger - 123h</li></ul>另外就是關於運算的部份,就如同 Expression Language(EL) 一樣,OGNL 提供了許多的運算:<br /><ul><li>add: 1+2, "hello"+"silver"</li><li>subtract: 2-1</li><li>multiply: 2*1</li><li>divide: 6/3</li><li>modules: 5%2</li><li>increment: a++, ++a</li><li>decrement: a--, --a</li><li>equality: a==b</li><li>less than: a<b</li><li>greater than: a>b</li></ul>2) 呼叫 method<br />對於熟悉 EL 的 programmer 來說,最希望的一件事就是 EL 能夠呼叫 method,不過很可惜的,目前為止,EL 還無法做到~不過別氣餒,OGNL 身為 EL 的強化版,對於 programmer 多年的渴望終於有了回應,OGNL 提供 programmers 可以呼叫 method 的功能,而且使用上相當的簡單。假設我們的 User class 有一個 method 為 toString(),如果我們在 OGNL 想要呼要的話,只要簡單的撰寫成:<br /><br /><span style="color: rgb(51, 51, 255);">user.toString()</span><br /><br />沒錯!就是這麼簡單,就如同我們在 Java 中撰寫呼叫 method 一樣~<br /><br />3) 存取 static method 與 field<br />OGNL 除了針對 instance 的 field 與 method 做呼叫外,對於 static 的 field 與 method 也是可以呼叫的:<br /><br /><span style="color: rgb(51, 204, 0);">@fullClassName@[field 或 method]</span><br /><br />不過要注意的是,我們在 struts.xml 檔案中需要在設定一個 constant:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">constant</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">struts.ognl.allowStaticMethodAccess</span><span style="color: rgb(42, 0, 255);">"</span><br /> value=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">true</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />這樣我們才可以開始使用 OGNL 呼叫 static method。假設我們在 User class 中有一個 static int age,我們在 OGNL 中就可以這樣呼叫:<br /><br /><span style="color: rgb(51, 51, 255);">@silver8250.bean.User@age</span><br /><br />要注意的部份只有 static 呼叫是要用 @ 符號來呼叫,這點記住就好了!其他的呼叫都是一樣的,當然我們可以取得某個 static 物件後,再採用原來的呼叫方式取得更進一步的 field 等:<br /><br /><span style="color: rgb(51, 51, 255);">@silver8250.bean.User@nextUser.name</span><br /><br /><span style="color: rgb(255, 0, 0);">NOTE:</span><br />請盡量保持 JSP 頁面的簡單,雖然 OGNL 擁有運算的能力,不過我還是建議你將這些運算移到 action 中執行,畢竟 JSP 頁面屬於 View,他只負責將 data 顯示給使用者,我們不應該將運算的動作在這裡執行,這樣會破壞 MVC 原先美好的本意!除非有必要這樣做,否則請饒了日後 maintain 的人吧~<b><br /></b>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-40195118786225748292009-06-28T09:38:00.006+08:002009-07-02T16:04:46.995+08:00[Struts2] Miscellaneous Tags之前已經提到兩種類型的 Tags:<a href="http://silver8250.blogspot.com/2009/05/struts2-struts2-control-tags.html">Control Tags</a> 與 <a href="http://silver8250.blogspot.com/2009/05/struts2-struts2-data-tags.html">Data Tags</a>。在這裡我們所提到的 Tags 都不屬於前面所提到的兩種類型,所以我特別放在雜項 (miscellaneous) 討論:1) include tag, 2) URL tag 與 3) param tag。<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Include Tag</span></span><br />如果你熟悉 JSP 的話,include tag 你應該不陌生,因為在 JSTL 中也有提供類似的 tag:<jsp:include>,不過,Struts2 提供的 include tag 擁有更多更好的功能,也具備了 JSTL 的 include tag 所沒有提供的能力。<br />之前我們有提過 Action tag,與現在的 include tag 有些許的不同:include tag 可以引用任何的網路 resources,包含其所屬的 web app 或是外部的 resources 等;而 action tag 只能引用自己本身所屬的 web app 中的 actions,對於外部的 actions 則無法引用。<br />Include tag 只有一個 attribute 可以使用:value,在 value 中我們可以指定 URL、也可以是某個 action 等,如果我們要傳送 parameter 給 include 的頁面,我們可以配合 param tag 使用 (稍後提到):<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:include</span> value=<span style="color: rgb(42, 0, 255);">"MyAction.action"</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">param</span> name=<span style="color: rgb(42, 0, 255);">"id"</span> value=<span style="color: rgb(42, 0, 255);">"myID"</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:include</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br /><span style="font-size:130%;"><span style="font-weight: bold;">URL Tag</span></span><br />對於 Struts2 framework 來說,轉換 URL 到目標的 action 是主要的工作,因為 URL 是瀏覽器需要解析的,而背後的工 action 對應就由 Struts2 framework 幫我們完成!在 Struts2 中提供了這樣的 URL tag 幫助我們轉換 URL,任何的 resources 我們都可以交由 URL tag 幫我們轉換。<br /><a href="http://struts.apache.org/2.x/docs/url.html">URL 提供的 attributes</a>,我們透過 URL tag 幫我們將 action 轉換成正確可執行的 URL,然後我們在嵌入 <a> 中:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">a</span> href=<span style="color: rgb(42, 0, 255);">'<s:url action="myAction" />'</span><span style="color: rgb(127, 0, 85);">></span>My Action<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">a</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />如果我們要傳送 parameter 給目標的 URL,我們可以結合 param tag 並透過 var attribute 幫我們將 URL 暫存起來,最後在透過 property tag 幫我們取出暫存的 URL:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:url</span> action=<span style="color: rgb(42, 0, 255);">"MyAction"</span> var=<span style="color: rgb(42, 0, 255);">"tmpURL"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">param</span> name=<span style="color: rgb(42, 0, 255);">"id"</span> value=<span style="color: rgb(42, 0, 255);">"myID"</span><span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:url</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">a</span> href=<span style="color: rgb(42, 0, 255);">'<s:property value="#tmpURL"/>'</span><span style="color: rgb(127, 0, 85);">></span>My Action<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">a</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><span style="font-size:130%;"><br /><span style="font-weight: bold;">Param Tag</span></span><br />這個 tag 在之前就已經有稍微看過了,目的在於將 parameter 做傳送的動作,在這個 tag 中只有兩個 attributes:1) name 與 2) value。<br />我們可以想像就是將 key-value pattern 傳送給某個地方,就像我們在上面就提過的,我們可以和 include tag 或 URL tag 配合,產生更好用的功能!<br /><br />在這裡我們討論到三個會用到但又不屬於特殊功能的 tags,這三個 tags 都算常用,而且很好用!Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-54863097571122523042009-06-27T11:38:00.007+08:002009-09-17T23:28:42.637+08:00[Ontology] Ontology and OWL有人可能問我,怎麼會突然想要寫點不一樣的東西勒?畢竟我以前都沒有紀錄過關於 Ontology相關的文章,我怎麼會想要轉寫 Ontology 呢?其實,昨天剛領到碩士畢業證書,回想起在碩士生涯的兩年裡,我主要學的就是 Ontology,想想我怎能不紀錄起來呢?這樣好像有違交了我兩年的指導教授,所以在這裡我決定紀錄起來!<br /><br />-----以上都是廢話 以下才是正文-----<br /><br /><a href="http://en.wikipedia.org/wiki/Ontology_%28computer_science%29">Ontology</a> 是什麼?是一種概念,試圖將人類腦中與生活中的知識加以描述成電腦可以瞭解的東西,而這些知識都是相關於某個領域 (domain),並且定義出該 domain 中的概念 (concept) 與這些 concepts 之間的關聯 (relationship) 形成一個 domain ontology。所以,ontology 是一種知識的表示方式,在 AI 領域中試圖將這些知識,透過嚴謹的定義,讓電腦可以具有人類腦中的知識,提昇電腦的判斷能力。<br /><br />目前針對 ontology 的描述方法不只一種,不過現在最普遍也最標準的方法是採用 <a href="http://www.w3.org/TR/owl-features/">Web Ontology Language (OWL)</a>,別問我為甚麼不是 WOL 而是 OWL。在<a href="http://thesis.lib.ncu.edu.tw/ETD-db/ETD-search-c/view_etd?URN=965202082">我的碩士論文</a>中,提出一套監控系統,採用 OWL 建立受監控的環境,讓電腦瞭解目前環境中的知識,在配合 <a href="http://www.w3.org/Submission/SWRL/">Semantic Web Rule Language (SWRL)</a> 進行語意式推論,提供更有效也更精準的危險偵測。<br /><br />雖然 OWL 是以 XML 的形式儲存,而 XML 強調是一種人類與電腦都方便閱讀的格式,但如果其中包含了複雜的 tags,對於人類的閱讀也不是很方便!目前有一套圖形化工具 <a href="http://protege.stanford.edu/">Protege</a>,提供方便使用者修改 OWL,並且很容易的 maintain。透過這類型工具的協助下,我們可以很容易的撰寫出 OWL!<br /><br />OWL 使知識具有語意,是來自於其本身提供的 restriction,使用 OWL 中定義的 restriction,使得我們撰寫出的 ontology 能具有嚴謹的關係,例如:inverseOf、disjoinWith 等,電腦可以透過 restriction 推導出語意關係。舉例來說:人在房間,房間屬於房子的一部分,房子與家等價,所以人在家中。這類型的推導來自於 description language (DL) 的定義,但是這樣的推導關係僅限於在現有的知識中,對於產生新的知識卻是沒有辦法的!所以又衍生出新的推論語言 SWRL,提供 first-order logic (FOL) 也就是一階邏輯,補足 OWL 中無法自行產生新知識的不足,SWRL 針對 OWL 提供比前身 RuleML 更好的結合性,讓 rule 與 knowledge 容易的結合,使得 maintain OWL+SWRL 可以更容易達成。<br /><br />SWRL 其實也是一種 XML-based 的表示方法,也因為要直接撰寫出 XML 實為不易,Protege 也提供了 <a href="http://protege.cim3.net/cgi-bin/wiki.pl?SWRLTab">SWTLTab</a> plug-in 協助使用者開發,使用者在 Protege 撰寫 SWRL 是以 FOL 形式撰寫,例如:father(x,y) ^ brother(x,z) -> uncle(z,y) 表示 x 是 y 的 father,x 與 z 是 brother,所以推論出 z 是 y 的 uncle。<br /><br />在這裡我只想要淺淺的帶過 ontology 的概念,以後有機會在深入探討,不過還是提供一些有用的 reference:<br /><br /><a href="http://www-ksl.stanford.edu/people/dlm/papers/ontology-tutorial-noy-mcguinness.pdf">Ontology Development 101: A Guide to Creating Your First Ontology</a><br />這篇論文主要描述如何設計與開發 ontology,對於初學者很有幫助,文章中並以簡單的範例來帶領我們開發一個 domain ontology。Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com2tag:blogger.com,1999:blog-6990680403271933461.post-90404528159018376532009-05-13T16:02:00.005+08:002009-05-13T20:24:38.755+08:00[Struts2] Struts2 Control Tags先前我們已經看過了 Struts2 framework 所提供的 Data tags,主要針對資料在 ActionContext 與頁面之間的移動。在這裡我們將要看到的是 Control tags,顧名思義就與控制流程相關的 tags。官方網站中列出了 9 種的 Control tags,不過在這裡我只會介紹其中的 3 種:1) iterator, 2) if 與 3) else tag。<br /><br /><span style="font-weight: bold;font-size:130%;" >iterator tags</span><br />iterator 對於你來說應該不陌生,這個顧名思義就是用來進行迭代的 tag。iterator tag 可以針對 Collection, Map, Enumeration, Iterator 與 array 進行迭代,並且提供目前迭代的 status 讓 programmer 可以針對狀況進行特殊行為,而此 status 會儲存在 ActionContext 中讓 programmer 呼叫使用。例如 programmer 可以根據目前迭代的 status 讓 Collection 的顯示出現單數與奇數列的資料有不同的背景顏色等等功能。下面的範例中示範了所有 iterator tag 與 status 的狀態:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:iterator</span> value=<span style="color: rgb(42, 0, 255);">"list"</span> status=<span style="color: rgb(42, 0, 255);">"listStatus"</span><span style="color: rgb(127, 0, 85);">></span><br /> Count:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#listStatus.count"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> Index:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#listStatus.index"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> isEven:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#listStatus.even"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> isOdd:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#listStatus.odd"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> isFirst:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#listStatus.first"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> isLast:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#listStatus.last"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> Value:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">hr</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:iterator</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />iterator tag 透過 value attribute 取得該集合,我們並且指定將目前迭代的 status 儲存為 listStatus 變數,並且放置於 ActionContext。而 iterator status 有哪些狀態可以顯示呢?我們可以查詢 <a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/views/jsp/IteratorStatus.html">IteratorStatus</a> class,因為這是 status 的實際物件。<br />上面的範例中,我們的集合內儲存的是 String 物件,所以迭代時無須指定 property,如果你的集合內儲存的是某個 JavaBean 物件,那每一次迭代的 property 就直接呼叫該 property 即可!<br /><span style="font-weight: bold;font-size:130%;" ><br />if, elseif and else tags</span><br />if, elseif 與 else tags 其實也很簡單,其目的就像是我們在程式語言中的 if-elseif- else 語句一樣,話不多說,直接給個範例:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:if</span> test=<span style="color: rgb(42, 0, 255);">"age > 10"</span><span style="color: rgb(127, 0, 85);">></span>You are old<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:if</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:elseif</span> test=<span style="color: rgb(42, 0, 255);">"age < 10"</span><span style="color: rgb(127, 0, 85);">></span>You are young<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:elseif</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:else</span><span style="color: rgb(127, 0, 85);">></span>You are 10 years old<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:else</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />我們在 test attribute 中利用 OGNL expression 進行條件測試,OGNL 跟 EL 一樣有提供 boolean operation 的功能。上面的範例就是測試 age property。<br /><br />在這裡我們簡介了兩個 Struts2 的 Control tags,這裡的 tags 有些用法跟 <a href="http://java.sun.com/products/jsp/jstl/">JavaServer Page Standard Tag Library(JSTL) </a>其實是很類似的!所以有寫過 JSTL 的人應該會感到很熟悉也很容易上手。Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-70591923450490948572009-05-12T10:13:00.015+08:002009-06-28T09:55:23.368+08:00[Struts2] Struts2 Data Tags在之前的文章裡,我們已經看過了 Action, Interceptor 與 OGNL 等。這些主要是整個 Struts2 framework 中的核心 components,而且在 MVC 中主要 focus 在 Model 與 Controller 的角色。現在我們要切換 MVC 角色中不可或缺,對於使用者來說是很重要的 View 部份!<br />Struts 2 framework 中提供了很多的 <a href="http://struts.apache.org/2.x/docs/tag-reference.html">tags</a> 可以使用,我將這些 tags 中我們在這裡先介紹跟 data 相關的部份,而所謂跟 data 相關是指這些 tags 讓我們可以很方便的將資料由 ValueStack 中搬進與搬出,以下將簡介 5 個重要的 data tags:1) property tag, 2) set tag, 3) push tag, 4) bean tag 與 5) action tag。<br />在進入正題之前,我還是要提醒一下,要使用 Struts2 framework 的 tags 之前,我們的 JSP 頁面要先撰寫 <a href="http://java.sun.com/products/jsp/syntax/2.0/syntaxref2011.html#8780">tag directive</a> 告知 JSP 我們要使用 Struts2 tags:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(127, 0, 85); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><%@</span><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"> taglib uri=</span><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(42, 0, 255); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">"/struts-tags"</span><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"> prefix=</span><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(42, 0, 255); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">"s"</span><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"> </span><span style="background: rgb(255, 255, 232) none repeat scroll 0% 0%; color: rgb(127, 0, 85); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">%></span><br /></pre><br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">property tag</span></span><br />property tag 在之前我們已經使用過很多次了,而這個 tag 應該是整個 Struts2 framework 中最常使用到的 tag 之一。property tag 提供了一種很快速很方便的機制,讓 programmer 可以很容易的將資料由 ActionContext 中移動,而資料的移動是根據 OGNL 中撰寫的 expression 來決定,資料也會根據 OGNL 的 Type Converter 進行轉換型態。不過有一點要注意的是,如果 OGNL 指到的 Java type object 沒有對應的 converter 的話,OGNL 預設是呼叫該物件的 toString() method。<br /><a href="http://struts.apache.org/2.x/docs/property.html">關於 property tag 中的相關設定請參考 Struts2 官方網站</a>,在此就不贅述了!<br />以下是一個簡單的使用 property tag 範例:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"user.age"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />上述的 tag 中我們撰寫 OGNL 要求將 ValueStack 中的 user.age 取出,並且轉換成 String 顯示!<br /><br /><span style="font-weight: bold;font-size:130%;" >set tag</span><br />對於 set 來說,意義在於將某個物件給予另一個新的名稱,有一些理由讓這個 tag 存在,例如我們如果用一個 OGNL expression 來取出資料時,expression 很長很容易撰寫錯誤時,這個 tag 就可以適時的發揮功用,減輕 programmer 的一些負擔。因為 set tag 可以將一些複雜又常用到的 OGNL 先給予新的 reference,而 programmer 在 set tag 之後就可以根據新的 reference 撰寫相對簡結的 OGNL expression。而且我們可以新的 reference 指定在 ActionContext 中的不同位置,這也可以將 data 複製到新的位置!透過 set tag 的 scope attribute 我們可以指定新的 data 要被存放在 JSP 中就有的 scope (application, session, request, and page) 或是 Struts2 中特定的 action scope。<br /><a href="http://struts.apache.org/2.x/docs/set.html">其餘的 attribute 在這</a>。以下就給個範例,不過我們沒有複雜的 OGNL:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:set</span> name=<span style="color: rgb(42, 0, 255);">"myUser"</span> value=<span style="color: rgb(42, 0, 255);">"user"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:set</span><span style="color: rgb(127, 0, 85);">></span><br />Your Name:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#myUser.name"</span><span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />我們將 ValueStack 的 user 物件重新存放,並且給予 myUser 的變數名稱,因為我們的 scope 是採用預設的 action,所以之後的 OGNL 要使用的話,就必須以 # 開頭!在之前的<a href="http://silver8250.blogspot.com/2009/04/struts2-ognl.html">深入探討 OGNL</a> 中我們有提到,如果要在 OGNL 中取得 session 的 data 就必須是 #session 開頭,在這裡有去的是,如果 set tag 將新的 data 除存在 action scope 的話,意思是 myUser 物件與 session 是相同的高度,也就是 ActionContext 中的最上層!不過要注意的是,如果我們將 data 放至於 action scope,其生命週期跟 request 是一樣的喔!如果要更長的生命週期,那就選擇使用 session 或 application 囉~<br /><br /><span style="font-weight: bold;font-size:130%;" >push tag</span><br />set tag 讓你可以將 data 建立一份副本,而且可以設定放置在哪個 scope;而 push tag 也有類似的功能,不過是將 data 放置於 ValueStack 中,這個 tag 也是很有用的,而且可以有更短的生命週期、更簡便的 OGNL expression。<a href="http://struts.apache.org/2.x/docs/push.html">完整的 tag reference</a>。<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:push</span> value=<span style="color: rgb(42, 0, 255);">"user"</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"name"</span><span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:push</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />上面的範例顯示了 push tag 在內的生命週期,也就是說,在 push tag 中我們可以不用重新撰寫 user 為起始的 OGNL expression,而我們可以直接取 user object 中的 name property,當然,離開 push tag 之後,我們就不能使用了,所以 push tag 有暫存變數的功能,並且讓我們的 OGNL expression 更簡潔有力!<br /><br /><span style="font-weight: bold;font-size:130%;" >bean tag</span><br />bean tag 看上去就像是 set 與 push tag 的混合版,主要的不同在於我們可以不用再現存的 data 上進行運作,我們可以自己創造一個新的 data,而且可以存放在 ValueStack 或是 ActionContext 中。預設是存放在 ValueStack 中,就如同 push tag 的範例一樣,是在 tag 的開始與結尾之間的 scope 使用!如果我們我要將 data 存放在 ActionContext 中,我們就要指定 id attribute:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:bean</span> name=<span style="color: rgb(42, 0, 255);">"silver8250.bean.HelloBean"</span> id=<span style="color: rgb(42, 0, 255);">"myBean"</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">param</span> name=<span style="color: rgb(42, 0, 255);">"content"</span><span style="color: rgb(127, 0, 85);">></span>Ya!<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">param</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">param</span> name=<span style="color: rgb(42, 0, 255);">"name"</span><span style="color: rgb(127, 0, 85);">></span>Silver<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">param</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"sayHello()"</span><span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:bean</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#myBean.content"</span><span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />上面的範例中,我們建立一個 HelloBean 並且包含 content 與 name properties,還有一個 sayHello() method。透過 bean tag 我們建立出新的 HelloBean 物件命名為 myBean,接著透過 param tag 將 name 與 content properties 賦予 value,並且在 bean tag 生命週期中呼叫 sayHello() method,最後,我們透過 property tag 呼叫儲存在 ActionContext 中的 myBean。<br /><span style="font-size:130%;"><br /><span style="font-weight: bold;">action tag</span></span><br />action tag 讓我們在頁面中呼叫不同 Action。使用 action tag 中最重要的 executeResult attribute,如果我們將 executeResult 設定為 true,則 action tag 就不只是幫我們執行 Action,還會將執行結果列印在畫面上!不過不用擔心,executeResult 預設是 false。一開始可能會覺得 action tag 不是那麼實用,不過當我們有一些 data 放至於某個 Action 中,並且是用來初始畫頁面的 data,那 action tag 就會非常的實用!以下就給個簡單的範例:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:action</span> name=<span style="color: rgb(42, 0, 255);">"Included"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:action</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"#request.message"</span><span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />我們採用 action tag 呼叫 IncludedAction,此 Action 會將一個 message 儲存在 request 中,不過我們現在不將結果印出來,所以採用 executeResult 的預設。使用 action tag 後,IncludedAction 會被執行,然後我們就可以由 request 取得 data。<br /><br />在這裡我們簡介了 Struts2 framework 中重要的 data tags,當然,還有其他的 tags 我在之後會慢慢的介紹。Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-66717284396632993212009-04-30T10:56:00.016+08:002009-05-04T13:42:00.106+08:00[Struts2] 深入探討 OGNL在之前我們已經知道 OGNL expression 可以幫助我們將 data 在網頁上與後端 Javabean 之間作搬移與轉換型態,而且我們也瞭解到我們的 Actions 會被放在 ValueStack 中讓 OGNL 可以存取。現在我們要告訴你,OGNL 可以存取任何集合的物件,而 ValueStack 只是其中一個集合物件而已!更完整的來說,OGNL 是用來存取 ActionContext 中的 data,而 ActionContext 中包含了 ValueStack。<br /><br /><span style="font-weight: bold;font-size:130%;" >ActionContext</span><br />ActionContext 儲存了 Struts2 framework 中所有的 data,這些 data 包含 ValueStack、Request、Session 與 Application 等。而所有的 OGNL 都會到 ActionContext 中查詢 data,不過如果我們沒有指定 OGNL 要到哪裡存取資料的話,OGNL 預設是到 ValueStack 中進行存取。所謂沒有指定的意思是,如果我們撰寫的 OGNL 為:<span style="color: rgb(51, 102, 255);">user.age</span>,這就是採用 OGNL 預設的存取位置。如下圖。<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGOUaIR858Z8B_27jiVwpVNZ0bX9JAjgcZGWrFyZ1JyT1Q0Syw3VHmPB8_mDYMS2nl3RiqiErzel2EwdehMKCpEopcRQq2_Ff8-4wMQos1vMfXogsfl6SzhYT0ts1cIw78fSWI3Adc9UM/s1600-h/actionContext.bmp"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 141px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGOUaIR858Z8B_27jiVwpVNZ0bX9JAjgcZGWrFyZ1JyT1Q0Syw3VHmPB8_mDYMS2nl3RiqiErzel2EwdehMKCpEopcRQq2_Ff8-4wMQos1vMfXogsfl6SzhYT0ts1cIw78fSWI3Adc9UM/s400/actionContext.bmp" alt="" id="BLOGGER_PHOTO_ID_5330378227867912018" border="0" /></a>在這裡所謂的 Context 不是指環境,而是一種有 container 概念的物件,主要是蒐集整個 framework 在執行時所需要的 data 與 resources。所有的 OGNL 都會先選擇所要存取的初始地點,如果沒有指定,就是到 ValueStack 中存取。如果我們將之前的 User 物件儲存在 session 中,那我們要存取 session 中 user 的 age,那我們就要改用:<span style="color: rgb(51, 102, 255);">#session['user'].age</span>。我們採用 # 開頭並且指定我們要存取的位置為 session,因為 session 是一個 Map 物件(回想我們之前所使用 SessionAware interface 中,session 物件是以 Map 方式被宣告!),我們要採用之前在<a href="http://silver8250.blogspot.com/2009/04/struts2-ognl-type-converter.html">內建的 OGNL Type Converter</a> 中提到的 Map property 存取方式,所以就會是 #session['user'],取得 user 物件後我們就可以操作內部的 properties!<br />那我們還有哪些存取位置可以使用呢?下面就列出可用的位置:<br /><ul><li>parameters - 此 request 中的 parameter map</li><li>request - 如同 JSP 中的 request</li><li>session - 如同 JSP 中的 session</li><li>application - 如同 JSP 中的 application</li><li>attr - 根據 page, request, session 與 application 的順序查詢第一個找到的 data</li><li>ValueStack - 預設的存取位置</li></ul><span style="font-weight: bold;font-size:130%;" >ValueStack</span><br />之前我們已經討論過很多關於 ValueStack 的資訊,現在我們要深入的討論 ValueStack。當 Strus2 framework 接收到使用者的 request 後,會先建立一個 ActionContext、ValueStack 與使用者呼叫的 action 物件,並且將 action 中的 properties 儲存到 ValueStack 中。這樣使用者就可以透過 OGNL 存取 ActionContext 中的 data。不過這裡有一點需要注意的是,在 ValueStack 中的 properties 只會出現最上層的 properties,也就是說如果有兩個 properties 是相同的變數名稱,只有最上層的 property 可以被存取。<br />你可能會好奇為什麼會有這樣情形發生呢?一個 class 中不可能有兩個變數是相同的名稱阿!沒錯!這的確有點弔詭,不過確實是有可能發生的,當我們的 Action 中的 Javabean property 採用 ModelDriven 的方式就會有可能:<pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> ModelDrivenAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport <span style="color: rgb(127, 0, 85); font-weight: bold;">implements</span> ModelDriven<User><br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> User user = <span style="color: rgb(127, 0, 85); font-weight: bold;">new</span> User();<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> name;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> getName()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> name;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setName(<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> name)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.name = name;<br /> }<br /> @Override<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> User getModel()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user;<br /> }<br /> @Override<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> execute() <span style="color: rgb(127, 0, 85); font-weight: bold;">throws</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Exception</span><br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">System</span>.out.println(<span style="color: rgb(42, 0, 255);">"This.name = "</span>+<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.name+<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">\n</span><span style="color: rgb(42, 0, 255);"> User.name="</span>+<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user.getName());<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> SUCCESS;<br /> }<br /><br />}<br /></pre><br />上面的 ModelDrivenAction 中我們採用 ModelDriven 方式讓 user property 直接存取,然後我們在 execute method 中印出使用者在 textfield 中輸入的值會存在哪個 property 之下,回想一下我們如果使用 ModelDriven 方式,我們的 OGNL 就可以直接寫成:<span style="color: rgb(51, 102, 255);">name </span>而不是 <span style="color: rgb(51, 102, 255);">user.name</span>,從 console 中可以發現,ModelDrivenAction 自己定義的 name property 被隱藏了!所以使用者輸入的值只會被除存在 user property 下的 name property!所以在 ValueStack 中會如下:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC_O3W130ocQlV64ETYGsC_lNqTgITVyKaPLXK74BQUnQy2eOszElauPUqnOHFj9-SG0-X4kVIguIOUtMdgyQa5-RWu5Rx1D172KKpnTbAbAdcHk9zQOXKyunhJ5DXFk9xhcF-GWgHyUw/s1600-h/2.bmp"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 215px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC_O3W130ocQlV64ETYGsC_lNqTgITVyKaPLXK74BQUnQy2eOszElauPUqnOHFj9-SG0-X4kVIguIOUtMdgyQa5-RWu5Rx1D172KKpnTbAbAdcHk9zQOXKyunhJ5DXFk9xhcF-GWgHyUw/s400/2.bmp" alt="" id="BLOGGER_PHOTO_ID_5331839472672150658" border="0" /></a></pre>所以我在<a href="http://silver8250.blogspot.com/2009/02/struts2-action-property.html">將物件作為 Action property</a> 中建議採用 Object-backed Javabean property 方式,這樣可以減少日後維護的問題!<br /><a href="http://silver8250.blogspot.com/2009/02/struts2-action-property.html"></a>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-19132015552942170092009-04-21T13:41:00.016+08:002013-06-26T01:06:42.002+08:00[Struts2] 建立自訂的 OGNL Type ConverterStruts2 framework 中的 OGNL Type Converter 已經滿足了我們大部分的情況,基本上我們可以不用自己建立自訂的 Type Converter,不過 Struts2 framework 還是提供了讓我們可以自訂 Type Converter 的機制!在這裡我將示範要如何建立一個自訂的 OGNL Type Converter!範例中主要是提供一個 UserTypeConverter:將頁面上的 User data 轉換成 Action 中的 User property。<br />
<br />
<span style="font-size: 130%; font-weight: bold;">Implement a type converter</span><br />
Struts2 framework 中,所有的 OGNL Type Converter 都必須 implements <a href="http://www.opensymphony.com/ognl/api/ognl/TypeConverter.html">TypeConverter </a>interface,這個 interface 提供了 programmers 在任何兩種類型的 data 進行轉換,不過在 Web application 中,我們只需要在 String 與 Object 之間作轉換就可以了!所以,我們自訂的 Type Converter 就採用 Struts2 framework 定義的 <a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/util/StrutsTypeConverter.html">StrutsTypeConverter</a>。StrutsTypeConverter 是一個 Abstract class,我們的 Type Converter extends StrutsTypeConverter 之外,還需要實做出兩個 abstract methods:<br />
<pre>public abstract <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html" title="class or interface in java.lang">Object</a> <b>convertFromString</b>(<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Map.html" title="class or interface in java.util">Map</a> context,
<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html" title="class or interface in java.lang">String</a>[] values, <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html" title="class or interface in java.lang">Class</a> toClass)
public abstract <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html" title="class or interface in java.lang">String</a> <b>convertToString</b>(<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Map.html" title="class or interface in java.util">Map</a> context, <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html" title="class or interface in java.lang">Object</a> o)</pre>
這兩個 methods 從 method name 就可以很清楚的瞭解其目的,convertFromString 主要是將頁面上 String-based data 轉換成 Java type property;而 convertToString 就是將 Java type property 轉換成 String-based data。<br />
<br />
Convert between Strings and User<br />
接下來就整個重頭戲了!我們要實際的撰寫我們的 Type Converter:<br />
<pre style="-moz-background-clip: -moz-initial; -moz-background-inline-policy: -moz-initial; -moz-background-origin: -moz-initial; background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: black;"><span style="color: #7f0055; font-weight: bold;">public</span> <span style="color: #7f0055; font-weight: bold;">class</span> UserTypeConverter <span style="color: #7f0055; font-weight: bold;">extends</span> StrutsTypeConverter
{
@Override
<span style="color: #7f0055; font-weight: bold;">public</span> <span style="color: #7f0055; font-weight: bold;">Object</span> convertFromString(Map context, <span style="color: #7f0055; font-weight: bold;">String</span>[] values, <span style="color: #7f0055; font-weight: bold;">Class</span> toClass)
{
<span style="color: #7f0055; font-weight: bold;">String</span> name = values[0];
<span style="color: #7f0055; font-weight: bold;">String</span> password = values[1];
User user = <span style="color: #7f0055; font-weight: bold;">new</span> User();
user.setName(name);
user.setPassword(password);
<span style="color: #7f0055; font-weight: bold;">return</span> user;
}
@Override
<span style="color: #7f0055; font-weight: bold;">public</span> <span style="color: #7f0055; font-weight: bold;">String</span> convertToString(Map context, <span style="color: #7f0055; font-weight: bold;">Object</span> o)
{
User user = (User) o;
<span style="color: #7f0055; font-weight: bold;">return</span> <span style="color: #2a00ff;">"User:name="</span>+user.getName()+<span style="color: #2a00ff;">", password="</span>+user.getPassword();
}
}</pre>
<br />
首先,就如我們之前提到的,我們的 Type Converter 要 extends StrutsTypeConverter class,然後實做出兩個 abstract methods。將字串轉換成物件的 method 中,我們主要是將使用者在頁面上輸入兩個字串,而這兩個字串會分別變成 User 物件中的 name 與 password!在這裡沒有什麼邏輯可言,因為我們只是要示範 Type Converter 罷了!反之,將物件轉換成字串時,我們就是將 User 物件顯示成一個字串,這就有點像是 toString() method 一樣,純粹將物件中的資訊轉換成字串!<br />
<br />
<span style="font-size: 130%; font-weight: bold;">Configure our type converter</span><br />
撰寫完我們自訂的 Type Converter 之後,那我們要如何讓 Struts2 framework 知道呢?我們需要一個設定檔!在 Struts2 framework 中提供了兩種方式讓我們可以設定我們的 Type Converter :1) Property-specific 與 2) Global type converter。<br />
<br />
<span style="font-weight: bold;">1. Property-specific</span><br />
這種方式的設定檔只能針對某個 Action 的某個 property 給予我們的 Type Converter!首先,我們要先建立一個設定檔,檔案的命名規則為:<span style="color: #3366ff;">{ActionName}-conversion.properties</span>。假設我們有一個 Action 為 UserTypeAction,此 Action 中當然要有 User property,然後我們就必須建立一個設定檔名為:UserTypeAction-conversion.properties,而這個 properties 檔案要跟 UserTypeAction 在同一個資料夾中!在設定檔中我們要撰寫如下:<br />
<pre style="-moz-background-clip: -moz-initial; -moz-background-inline-policy: -moz-initial; -moz-background-origin: -moz-initial; background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: black;"><span style="color: #2a00ff;">user</span>=silver8250.type_converter.UserTypeConverter</pre>
<br />
其中,先告知 Struts2 framework 我們要進行轉換的 property 為 user,然後在等號右邊給予我們的 Type Converter!這樣就完成了設定檔案!<br />
這種設定檔到底有什麼好處呢?一開始你應該會很困惑,因為我們不可能為每一個 Action 中有使用 User property 都增加一個設定檔,我們一定是會使用等等會談到的 Global 設定檔!不過在某一種情況下會有用處!假設我們有兩個 Type Converters,第一個是 Global 的,所以我們採用 Global 的設定讓大多數的 property 都使用,不過如我們有某個 Action 的某個 property 要使用我們設計的第二個 Type Converter,這時候 property-specific 就派上用場了!因為 Struts2 framework 會先以 property-specific 設定優先!<br />
<br />
<span style="font-weight: bold;">2. Global type converter</span><br />
另一種設定檔就是 Global 型態的 Type Converter,我們只需要一個設定檔為:xwork-conversion.properties 檔案,並且放在 WEB-INF/classes 之下就可以!內容如下:<br />
<pre style="-moz-background-clip: -moz-initial; -moz-background-inline-policy: -moz-initial; -moz-background-origin: -moz-initial; background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: black;"><span style="color: #2a00ff;">silver8250.bean.User</span>=silver8250.type_converter.UserTypeConverter</pre>
<br />
等號左邊是告訴 Struts2 framework 是哪一個 Object 要執行 Type Conversion,右邊則是要使用哪個 Type Converter 進行轉換!這樣就完成了 Global 的設定檔!大多數的情況下我們都是以 Global 設定檔為主!<br />
<br />
Use our type converter<br />
最後就是我們在頁面上要如何撰寫呢?其實沒有任何的差別!例如:我們的 Type Converter 是要讓頁面上的兩個字串轉換到 User 中的 name 與 password,所以我們在頁面上就要設計兩個 textfield 讓使用者輸入:<br />
<pre style="-moz-background-clip: -moz-initial; -moz-background-inline-policy: -moz-initial; -moz-background-origin: -moz-initial; background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: black;"><span style="color: #7f0055;"><</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:</span><span style="color: #7f0055; font-weight: bold;">form</span> action=<span style="color: #2a00ff;">"UserType"</span> method=<span style="color: #2a00ff;">"GET"</span><span style="color: #7f0055;">></span>
<span style="color: #7f0055;"><</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:textfield</span> name=<span style="color: #2a00ff;">"user.name"</span> label=<span style="color: #2a00ff;">"User Name"</span><span style="color: #7f0055;">></span><span style="color: #7f0055;"></</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:textfield</span><span style="color: #7f0055;">></span>
<span style="color: #7f0055;"><</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:textfield</span> name=<span style="color: #2a00ff;">"user.password"</span> label=<span style="color: #2a00ff;">"User Password"</span><span style="color: #7f0055;">></span><span style="color: #7f0055;"></</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:textfield</span><span style="color: #7f0055;">></span>
<span style="color: #7f0055;"><</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:submit</span><span style="color: #7f0055;">></span><span style="color: #7f0055;"></</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:submit</span><span style="color: #7f0055;">></span>
<span style="color: #7f0055;"></</span><span style="color: #7f0055; font-weight: bold;">s</span><span style="color: #7f0055;">:</span><span style="color: #7f0055; font-weight: bold;">form</span><span style="color: #7f0055;">></span></pre>
<br />
你沒看錯!真的沒有差別!真的~<br />
<br />
在這裡我們示範了要怎樣設計自己的 Type Converter,不過大多倏地行況下,我們不需要這樣作!Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com1tag:blogger.com,1999:blog-6990680403271933461.post-81545374007526657702009-04-15T09:54:00.001+08:002009-04-16T11:00:16.575+08:00[Struts2] 內建的 OGNL Type Converter<span style="font-family:verdana;">我們已經了解到 OGNL 具備有自動的 data transfer 以及 type converter 的功能,現在我們就要介紹</span> OGNL 中內建的 type converter。之前我們 <a href="http://silver8250.blogspot.com/2009/04/struts2-introduce-ognl.html">Introduce OGNL</a> 中就有簡單的使用過將頁面上 String-based data 轉換成 Javabean 中的 int type data。讓我們來看看這樣的轉換機制到底有哪些其他的功能!<br /><br /><span style="font-weight: bold;font-size:130%;" >All converters</span><br />首先我們先看看 Struts2 framework 中到底有哪些內建的 converters:<br /><ul><li>String - 字串,幾乎是不用進行轉換,因為在 client 端與 server 端的型態就是一樣的!<br /></li><li>boolean/Boolean - true 或 false 的字串會被轉換<br /></li><li>char/Character - 可以想像是一個單位的字串<br /></li><li>int/Integer, float/Float, double/Double, long/Long - 原始的格式被轉換成字串型態,你可以想像就像是使用 String.valueOf() 來進行轉換!<br /></li><li>Date - 根據使用者目前的 Locale 而被轉換成 SHORT 格式的字串,例如:28/02/97<br /></li><li>array - 每一個 array 中的 element 將會被轉換成 String 物件做處理<br /></li><li>List - 預設設定的 Element 以 String 為主<br /></li><li>Map - 預設設定的 Element 以 String 為主</li></ul>上述的各種就是 OGNL 內建的 converters,基本上 Primitive type(例如:String, int 等) 是比較好處理也比較可以理解其運作原理;而 Colleciton type(例如:array, List 與 Map) 在轉換上比較複雜,但是對於 programmers 來說還是很直觀的!至於我們要怎樣讓 OGNL 可以使用適當得 type converter,這點我們就無須擔心,只要我們將要取的 data 放置到 ValueStack 中並且在 view-layer 中撰寫適當的 OGNL expression language 就可以了,OGNL 會自動知道使用哪個 type converter!<br />以下我們將所有的 type converter 分成兩類加以討論:1) Primitive type mapping 與 2) Collection type mapping。<br /><br /><span style="font-weight: bold;font-size:130%;" >Primitive type mapping</span><br />基本型別的轉換是最簡單的,也是很直觀的!我們就直接給個範例:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span> action=<span style="color: rgb(42, 0, 255);">"Register"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.name"</span> label=<span style="color: rgb(42, 0, 255);">"Name"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.password"</span> label=<span style="color: rgb(42, 0, 255);">"Password"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.age"</span> label=<span style="color: rgb(42, 0, 255);">"Age"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:submit</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />上面所顯示的 form 當使用者按下 submit 之後,就會根據 action attribute 中的 value 將表單傳送給 struts.xml 中設定的 Register action,透過 OGNL 的 expression language 會將各個欄位的值交由 param interceptor 找尋 mapping destination,在由 OGNL 個別進行 type conversion。由上述的 OGNL 中我們可以知道 Register action 中必定會有一個 User object 作為 property:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> RegisterAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> User user;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setUser(User user)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user = user;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> User getUser()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user;<br /> }<br />}<br /></pre><br />我們現在是透過 Object-back Javabean property 的方式(<a href="http://silver8250.blogspot.com/2009/02/struts2-action-property.html">將物件作為 Action property</a> 中提到)存取 User property。而 User object 中一定會有 name, password 與 age 的 property。所以 OGNL 會先透過 RegisterAction 取得 User 物件後,在依照 expression 中不同的 property 去進行 type conversion,例如:age 在 User 物件中屬於 int 型態,則會啟動 int type converter 將使用者輸入的 String type 的 age 轉換成 int type 的 age。<br />至於為甚麼 HTTP 中的 form 欄位一定都是 String 呢?這點就要牽涉到 HTTP 本身的設計,因為我們傳送的 form 欄位都會以 parameter 的方式傳送,只是有 GET 跟 POST 的差別,這兩種差別在於 GET 會將這些 key value pairs 顯示在 URL 上;而 POST 不會!但是最終都是以 parameter 的方式傳送!也正因為如此,Java EE 中設計的 <a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/http/HttpServletRequest.html">HttpServletRequest</a> 物件有一個 getParameter() method 就是用來取得使用者在頁面上所傳送的資料。而這個 method 回傳的資料都是 String,所以 OGNL 必須將這些 String-based data 進行 type conversion 了!<br />同樣的,如果我們要取得某個 property value,我們在頁面上就可以使用 property tag 來取值:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span> action=<span style="color: rgb(42, 0, 255);">"Register"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.name"</span> label=<span style="color: rgb(42, 0, 255);">"Name"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.password"</span> label=<span style="color: rgb(42, 0, 255);">"Password"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.age"</span> label=<span style="color: rgb(42, 0, 255);">"Age"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:submit</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />這個 OGNL 在後端工作時會轉換成:<span style="color: rgb(51, 51, 255);">getUser().getAge();</span><br />另外,當 OGNL 在進行 type conversion 時也會進行 validation 的作業!舉個例子來說,如果使用者在 Age 欄位中輸入了非數字的字串,OGNL 在 type conversion 時就會出現錯誤,並且顯示錯誤訊息在使用者所輸入的欄位上,這樣的錯誤機制有點像我們在<a href="http://silver8250.blogspot.com/2009/02/struts2-action.html">深入實做 Action</a> 中提到的驗證失敗的訊息。<br /><br /><span style="font-weight: bold;font-size:130%;" >Collection type mapping</span><br />對於上面所提到的 primitive type conversion 的確比較直觀,不過對於 programmer 來說,要讓 String-based 的 HTTP data 能夠 mapping 到 Collection 類的 Java-type 的確比較棘手!好在 OGNL 已經提供了這樣的功能,讓 programmer 不必擔心這類型的轉換工作!<br />在 Struts2 framework 中提供了將 multivalued request parameters 轉換到有變化性的 Collection 型態 property,而且也包含了原始的 array 型態,畢竟 array 是所有 Collection-type 的基礎型態!以下將分成三種不同型態的 Java-type 分別介紹:1) array, 2) List 與 3) Map。<br /><br /><span style="font-weight: bold;">1. array</span><br />Array 其實也算是基本的 Java 型態,只是收集了一堆相同型態的資料罷了!Struts2 framework 提供了這樣的轉換功能,也就是如果我們的 Action 中宣告了 array property(又稱為 indexed Javabeans property),我們可以很輕鬆的完成由網頁上的資料轉換到 Javabean 中的 array。這樣的功能源自於 OGNL 的 navigate(導覽) 能力,因為具有導覽的功能,我們可以期望 OGNL 在 Collection-type 中幫我們找到某個 elements。話不多說,我們就看以下的範例吧:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span> action=<span style="color: rgb(42, 0, 255);">"Regist"</span> method=<span style="color: rgb(42, 0, 255);">"get"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"age"</span> label=<span style="color: rgb(42, 0, 255);">"age"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"age"</span> label=<span style="color: rgb(42, 0, 255);">"age"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"age"</span> label=<span style="color: rgb(42, 0, 255);">"age"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"name[0]"</span> label=<span style="color: rgb(42, 0, 255);">"Name"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"name[2]"</span> label=<span style="color: rgb(42, 0, 255);">"Name"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"name[3]"</span> label=<span style="color: rgb(42, 0, 255);">"Name"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"ageInt"</span> label=<span style="color: rgb(42, 0, 255);">"age int"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"ageInt"</span> label=<span style="color: rgb(42, 0, 255);">"age int"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"ageInt"</span> label=<span style="color: rgb(42, 0, 255);">"age int"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:submit</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />首先是頁面的部份,在上面我們宣告了一個 form,不過我們將資料傳送的方式設定為 GET 模式,因為這樣我們就可以觀察 array 型態的資料是怎樣被安排被傳送的。接下來就是重頭戲了!我們有三種 properties,age, name 跟 ageInt。在 age property 中我們測試不要給 index ,看看資料會怎樣被安排;另外就是 name property,這裡的寫法很像是在 Java 中對 array 給值的寫法,我們在這裡故意跳過 index=1 的 array!至於 ageInt 我等等會解釋!<br />接著就是 Javabean,我們看看 age 跟 name properties 要怎樣在 Javabean 中撰寫:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> RegistAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>[] age;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>[] name = <span style="color: rgb(127, 0, 85); font-weight: bold;">new</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>[4];<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span>[] ageInt;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>[] getAge()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> age;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setAge(<span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>[] age)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.age = age;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>[] getName()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> name;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setName(<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>[] name)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.name = name;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span>[] getAgeInt()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> ageInt;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setAgeInt(<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span>[] ageInt)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.ageInt = ageInt;<br /> }<br /> @Override<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> execute() <span style="color: rgb(127, 0, 85); font-weight: bold;">throws</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Exception</span><br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">for</span> (<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> i=0,n=<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.age.length;i<n;i++)<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">System</span>.out.println(<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.age[i]);<br /><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">for</span> (<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> i=0,n=<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.name.length;i<n;i++)<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">System</span>.out.println(<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.name[i]);<br /><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">for</span> (<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> i=0,n=<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.ageInt.length;i<n;i++)<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">System</span>.out.println(<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.ageInt[i]);<br /><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> SUCCESS;<br /> }<br />}<br /></pre><br />首先是 age,我們在這裡將他宣告為 Integer 的物件陣列,而 ageInt 則是宣告為 int 的原始型態陣列,等等我們會看到再取值時的差別!再來就是 name property,我們在這裡採用 String 型態,由於 String 不屬於 primitive type 或是 primitive type wrapper 所以我們要自行先宣告陣列大小!這點需要注意一下~否則在 assign value 時 console 會出現錯誤訊息!<br />在這裡使用陣列跟一般的 property 沒兩樣,我們不必擔心說 assign 具有 index 的 value,因為 OGNL 會幫我們處理,我們只要如同往常提供 getter/setter method 就可以!接下來就是要觀察一下這些 parameters 會怎樣傳送?我們就將 form submit 出去,由 URL 上方可以看到這些 parameters 的結果:<br />http://localhost:8080/HelloStruts2/Regist.action?<span style="color: rgb(51, 102, 255);">age=1&age=2&age=3&name[0]=4&name[2]=5&name[3]=6&ageInt=7&ageInt=8&ageInt=9</span><br />由於我們的 form 中有兩種類型的 textfield:一種是有給 index,另一種則是沒有!我們會看到沒有 index 的 value 會被照順序的安排,並且使用相同的 key(如上的 age 與 ageInt),而有給 index 的 value 則會按照我們的 index 作為 key(如上的 name[2])!在 Action 接收後,我們特別在 execute() method 中將這些 array values 印出來觀察,我們發現到 name[1] 確實是沒有值!<br />最後我們就要看看怎樣將這些 array values 顯示在頁面上:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">Age:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"age"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br />Name:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"name"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br />Age Int:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"ageInt"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /><br />Age[1]:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"age[1]"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br />Name[1]:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"name[1]"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br />Name[2]:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"name[2]"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br />Age Int[1]:<span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:property</span> value=<span style="color: rgb(42, 0, 255);">"ageInt[1]"</span><span style="color: rgb(127, 0, 85);">/></span><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">br</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />我們分成兩個部份,第一個部份是不給予 index 看看值會怎樣被取出來!第二部份就是給予 index 取出某個 element 的值!第二個部份很好猜測,就和我們先前將 value 寫回 action 中的方式一樣!不過第一部份就比較難猜了!我就直接執行看看結果吧!<br />Age:1<br />Name:ognl.NoConversionPossible<br />Age Int:7, 8, 9<br /> Age[1]:2<br />Name[1]:<br />Name[2]:5<br />Age Int[1]:8<br />第二部份的結果我就不多說了!只是提醒一下如果是 NULL 的值,回傳到頁面上就會是空白的內容。現在我們就探討一下第一部份的結果。首先,如果我們在 Action 中採用 Primitive type wrapper 物件(例如:Integer, Double 等)宣告陣列,當我們想要印出所有陣列中的元件,我們就要自行 iterate array,如同我們的 age property;如果我們是使用 primitive type 宣告陣列,Struts2 framework 就會自動幫我們 iterate array;另外,如果我們的 array 不屬於 primitive type 或 primitive type wrapper 的話,OGNL 就無法幫我們顯示內容了!<br /><br /><span style="font-weight: bold;">2. List</span><br />List 也是在 Struts2 framework 中有支援 navigate 功能,我們可以將上面的範例簡單的修改為 List:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> RegistAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>> age;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>> name;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>> getAge()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> age;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setAge(<span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">Integer</span>> age)<br /> { <br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.age = age;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>> getName()<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> name;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setName(<span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>> name)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.name = name;<br /> }<br />}<br /></pre><br />頁面的部份我們不必修改,因為在 Struts2 framework 中對於 List 與 array 的處理方式其實是類似的,上面的例子中我們使用 Primitive type 作為 List 中的 Element,我們也可以使用非 Primitive type,只要我們使用 J2SE 5.0 的重要特性-Generic 就可以輕鬆完成!如下的 Action 讓我們可以在 List 放我們自己定義的物件:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> RegistAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><User> users;<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><User> getUsers()<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> users;<br />}<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setUsers(<span style="color: rgb(127, 0, 85); font-weight: bold;">List</span><User> users)<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.users = users;<br />}<br />}<br /></pre><br />在頁面上我們就可以採用這樣的 OGNL expression language:<span style="color: rgb(51, 102, 255);">users[1].name</span><br /><br />3. Map<br />最後一種 OGNL 可以進行轉換的就是 Map,Map 跟使用 List 上沒有太大的差別,主要差別在於 Map 是以物件作為 key,而 List 是以 index 作為 key。<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> Map<String, User> users;<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> Map<Integer, User> otherUsers;<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> Map<String, User> getUsers()<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> users;<br />}<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> void setUsers(Map<String, User> users)<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.users = users;<br />}<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> Map<Integer, User> getOtherUsers()<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> otherUsers;<br />}<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> void setOtherUsers(Map<Integer, User> otherUsers)<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.otherUsers = otherUsers;<br />}<br /></pre><br />上面的 Action 中我們宣告了兩個 Map properties,第一個是用 String 作為 Map 的 Key,第二個則是用 Integer 作為 Map 的 Key。而這兩種 Map 都是存放 User 物件。而我們在頁面存取這兩種 Map 時就要撰寫成如下:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span> action=<span style="color: rgb(42, 0, 255);">"Regist"</span> method=<span style="color: rgb(42, 0, 255);">"get"</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"users['silver'].name"</span> label=<span style="color: rgb(42, 0, 255);">"Silver Name"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"users.kent.name"</span> label=<span style="color: rgb(42, 0, 255);">"Kent Name"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"otherUsers['0'].name"</span> label=<span style="color: rgb(42, 0, 255);">"Other User Name"</span><span style="color: rgb(127, 0, 85);">></span><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:textfield</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:submit</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85); font-weight: bold;">s</span><span style="color: rgb(127, 0, 85);">:</span><span style="color: rgb(127, 0, 85); font-weight: bold;">form</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />我們存取 Map 的方式可以用 [] 將 Key 寫在裡面,不過要加上 ' ' 符號!或者我們可以直接指定 Key(如:user.kent) 方式,但是當我們的 Key 採用 Integer 時,我們就只能使用第一種表示法存取了!<br /><br />在這裡我們討論了進階的 OGNL 的 type conversion 機制,從 primitive type 到 collection-based type,如:array, List 與 Map。OGNL 讓 programmers 可以不必煩惱如何將 String-based 的 HTTP value 轉換成 Java-type based 的 Javabean,甚至我們在撰寫 OGNL expression language 時也是很輕鬆容易的!Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com2tag:blogger.com,1999:blog-6990680403271933461.post-40757883276984020572009-04-14T12:24:00.003+08:002009-04-14T12:43:51.064+08:00[SQL] SQL Server 中如何知道某個 table 是否存在今天剛好碰到這樣的問題,以往我都是在既有的 tables 下對資料庫進行存取,不過因為系統中出現了暫存 table,所以我在每一次執行完成後就必須刪除(drop)該 table,也因為如此,我在 SQL 終究必須先判斷該 table 是否存在,如果存在就代表上次的作業沒有將此 table 刪除!<br />由於上述的原因,透過 Google 找到了相關的資源:<span style="font-size:100%;"><span style="font-weight: bold;"><br /><br /></span><a href="http://sqlserver2000.databases.aspfaq.com/how-do-i-determine-if-a-table-exists-in-a-sql-server-database.html">How do I determine if a table exists in a SQL Server database?</a></span><br /><br />這樣的 SQL 撰寫起來蠻容易的:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><br /><span style="color: rgb(127, 0, 85); font-weight: bold;">IF</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">EXISTS</span>(<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">SELECT</span> 1<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">FROM</span> INFORMATION_SCHEMA.<span style="color: rgb(127, 0, 85); font-weight: bold;">TABLES</span><br /><span style="color: rgb(127, 0, 85); font-weight: bold;">WHERE</span> TABLE_TYPE=<span style="color: rgb(42, 0, 255);">'BASE TABLE'</span><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">AND</span> TABLE_NAME=<span style="color: rgb(42, 0, 255);">'temp_table'</span><br />)<br /><span style="color: rgb(63, 127, 89);">--如果存在在</span><br /><span style="color: rgb(127, 0, 85); font-weight: bold;">DROP</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">TABLE</span> temp_table<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">ELSE</span><br /><span style="color: rgb(63, 127, 89);">--如果不存在在</span><br /></pre><br />上述的 SQL 應該還蠻容易瞭解的,透過 IF...ELSE 敘述並且配合 EXISTS 語法就可以用來判斷存在性,整個 SQL 語法中的核心在於 SELECT 語句中,我們在 SQL Server 中是要透過 <a href="http://msdn.microsoft.com/zh-tw/library/ms186778.aspx">INFORMATION_SCHEMA</a> 這個 system view 可以得知系統的中繼資料,透過 TABLES 就可以取得所有的 tables,然後我們在 WHERE 條件終將 TABLE_TYPE 設定為 BASE TABLE,這代表我們要查詢的是基本的 table,而不是 view;然後在告知 TABLE_NAME 為我們想要取得的 table 名稱,這樣就可以!至於為何是 SELECT 1 呢?這個數字 1 跟 C 語言中的 true 是類似的,因為在 SQL 中不存在 true 這樣的 boolean type,所以如果我們的 WHERE 條件成立,就會回傳 1 告知 EXISTS 為 true!<br /><br />今天學到的東西,與大家分享之~Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-55447957768712566552009-04-08T13:47:00.022+08:002009-05-04T13:42:47.000+08:00[Struts2] Introduce OGNLObject-Graph Navigation Language(OGNL) 是 Struts2 framework 中很重要的一個 component 之一,OGNL 負責作 data transfer 與 type conversion 的工作,並且讓 programmers 可以很簡單的將網頁上的 String-based data 轉換成 Java type-based data。其實,我們在之前就已經使用過 OGNL 將資料由 ValueStack 中搬進跟搬出,而且使用的方式很簡單!<br />OGNL 並不是 Struts2 framework 中所特別發明的,而是被 Struts2 framework 所整合的一種技術,從 programmer 角度來看,OGNL 包含了兩種功能:1) expression language 與 2) type converter。<br /><br /><span style="font-weight: bold;font-size:130%;" >Expression Language</span><br />如果你有用過 JSP 中標準的 EL 的話,這裡的 expression language 你應該就會有點感覺,因為 OGNL 的撰寫方式跟 JSP 中的 EL 是很類似的!OGNL 的 expression language 一種連結 HTTP 端的 string-based data 與 Server 端 Javabean 的 Java type-based data,並且可以將這兩端的 data 作相互的轉換。讓我們回顧一個小小的範例:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span>s:<span style="color: rgb(127, 0, 85);">property</span> value=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">user.age</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />這個 property tag 是之前我們就使用過的,我們的 OGNL 就是寫在 value attribute 中的 String,這個 OGNL 就是將目前 Action 中的 user property 取出,再進一步的取出 user 中的 age property,所以 user property 是另一個 Javabean。這個 OGNL 很簡單,但是 OGNL 也可以撰寫得很複雜,OGNL 本身有提供很多好用的 built-in 讓 programmers 可以很容易的存取 ValueStack 中的值!<br />這裡你可能會有一個問題:Struts2 framework 怎樣知道哪個 String 是屬於 OGNL 哪個 String 是真正的字串呢?其實這就是 Struts2 tag 本身的設定了,有些 tag 的某些 attributes 可以寫 OGNL,而有些只能是純字串,而 OGNL 為了讓 programmer 在純字串的 attribute 中可以撰寫,我們可以採用 %{ expression } 的方式,這樣就保證一定是 OGNL!所以我們也可以將上面的 OGNL 撰寫成這樣:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span>s:<span style="color: rgb(127, 0, 85);">property</span> value=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">%{user.age}</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><br />不過要知道哪個 tag 的哪些 attributes 是可以 parse 或不能 parse OGNL 語法,我們就要參考 Struts2 framework 官方文件的 <a href="http://struts.apache.org/2.x/docs/tag-reference.html">Tag reference</a>,這份文件包含了所有的 Struts2 tags,我們在這裡不會詳談,不過你可以點進去各個 tag 看看該 attribute 的 type,例如:property tag 的 value attribute 屬於 Object,這就代表 value attribute 可以 parse OGNL 語法!<br /><br /><span style="font-weight: bold;font-size:130%;" >Type Converter</span><br />OGNL 不僅僅是一種 expression language,他還會幫我們將資料型態進行轉換,也就是扮演著 type converter 的角色。其實在上面的範例中我們就示範了 OGNL 的 type conversion。因為在 user class 中的 age property 是屬於 int 型態,我們透過 OGNL 存取 Action 中的這個值,OGNL 就會幫助我們將 int 型態的 property 轉換成 string-based data,因為在 HTTP 之下,所有的資料都會是字串而已,但是在 server 端的 Javabean 中卻可以有很多種型態的資料!所以,OGNL 將資料由 Javabean 搬移到網頁上或是由網頁上搬移到 Javabean 中都會進行 type conversion。這樣的功能無須 programmer 的插手,OGNL 會自動的偵測 data 要轉換的型態!<br />在 Struts2 framework 中有針對 OGNL 的 type conversion 機制提供了很多種內建的 converter,當然,Struts2 framework 也有讓 programmer 可以自行定義 convert er 的功能!不過那是之後我們才會在提到了!<br /><br />由上面的敘述中我們瞭解了 OGNL 在 Struts2 framework 中所扮演的角色後,我們就要來描述一開始提到 OGNL 的另咦項中要的工作:Data transfer。也就是 OGNL 如何與 Struts2 framework 之間進行互動。讓我們先看看下圖:<br /><table style="width: auto;"><tbody><tr><td><a href="http://picasaweb.google.com/lh/photo/SYbWBs4N-EYr9DATddjsfg?authkey=Gv1sRgCIOHj-_kiuirTw&feat=embedwebsite"><img style="width: 415px; height: 279px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtkwxoDdR3hF6teR4P8pJkBRYyxhALK7Z23p4wxcHkzKIXnGBkyxtiqQcKeWtEANXxzlB21iOuCge9Uw1WorMTPNRWmJ5p5jYdZFeITIhlELf-2QGXqfZ7WmF1ZuL-vol2CZgo8B7C1kY/s800/1.jpg" /></a></td></tr><tr><td style="font-family: arial,sans-serif; font-size: 11px; text-align: right;">寄件者 <a href="http://picasaweb.google.com/silver8250.bboy/Silver8250?authkey=Gv1sRgCIOHj-_kiuirTw&feat=embedwebsite">阿信 (Silver8250) 的冷泡咖啡館~</a></td></tr></tbody></table><br />這張圖顯示了整個 data 如何在 OGNL 之下由 client 端到 server 端的進出!這裡所謂的 client 端就是圖中最下方的兩個 HTML 檔案,因為這最終還是使用者看到的格式。以下我們將這圖中的兩個部份分開介紹:1) Data in 與 2) Data out。<br /><br /><span style="font-weight: bold;font-size:130%;" >Data In</span><br />一開始使用者會先看到 index.html,當然,這個檔案是我們的 JSP 檔案,不過透過使用者的瀏覽器看到則是一些純粹的 HTML。然後使用者透過瀏覽器輸入資料後,就會將資料轉交給 Servlet,透過 HTTP 方式以 key value pair 方式傳送,就如圖中的 Servlet Request 的內容,Servlet Request 會將這些 key value pairs 轉交給 OGNL Expression Language and Type Converter 作處理,接下來就是 OGNL 的工作了!<br />首先,OGNL 會知道資料要去 ValueStack 中存取,並且 OGNL 會自動的知道要存取在 ValueStack 中的哪個 Action 物件,如同圖中的 HelloUser 這個 Action 物件。接下來就是 param interceptor 的工作了,我們在<a href="http://silver8250.blogspot.com/2009/03/struts2-interceptors.html">內建的 Interceptors</a> 中提到 param interceptor,他會根據 OGNL expression 將資料與 ValueStack 中的 Action 物件作 mapping,並且找出該 data 最終要存放的位置。例如 param interceptor 會將圖中的 user.age 對應到 HelloUser 中 User 物件的 age property。<br />一旦 param interceptor 決定好該 data 的最後歸宿,OGNL 就會負責呼叫該 property 的 setter method,不過我們原始的資料是 string-based data,在 User 的 age property 確定 int 型態,這時候就會用到 OGNL 的 Type Converter,OGNL 會先使用內建的 converter 來試圖轉換型態,如果沒有才會用使用者自訂的 converter 來轉換,如果都沒有辦法轉換,則會拋出 Exception。最後 string-based 的 "24" 會被轉換成 Java type 的 int 型態的 24,並且儲存到 ValueStack 中。<br />在這裡我們不用知道太多關於 ValueStack 的內容,我們現在只要知道 ValueStack 就是一個 Stack 的容器,他會負責儲存所有的 Action 物件等等。如果有兩個物件都有 age property,則只會有一個 property 會被存取!<br /><br /><span style="font-weight: bold;font-size:130%;" >Data Out</span><br />至於另一半的作業其實也是很類似,只是方向相反罷了!當 Struts2 framework 接收到使用者的 request 後會執行我們之前所談到的 interceptor stack,並且執行 Action 中定義的 business logic,當產生了使用的 Result 頁面後,OGNL 就會開始啟動作業了!就如同圖中的 Welcome.jsp 頁面,裡面宣告了 OGNL expression:user.age,OGNL 就會根據 expression 到 ValueStack 中取得確實的值,並且將這些值轉型成 String 型態。最後就會變成使用者瀏覽器看到的 Welcome.html 的內容。<br /><br />在這裡我們瞭解到 OGNL 在 Struts2 framework 中的主要工作以及其扮演的角色,並且瞭解到 OGNL 如何與 Struts2 framework 中的 component 互動的資訊。Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-49789067828826863662009-04-06T23:35:00.018+08:002009-04-07T17:47:13.129+08:00[Struts2] 建立自訂的 Interceptor我們已經介紹了 Struts2 framework 中很重要的 component 之一的 Interceptor 的重要特性,雖然 Struts2 framework 中已經有很多內建的 interceptors 幫助我們減少很多工作,但是有時候我們還是有自己的特殊需求,所以我們就必須自己撰寫我們的 interceptor。<br />根據我們之前在<a href="http://silver8250.blogspot.com/2009/03/struts2-interceptor.html">深入探討 Interceptor</a> 中提過的,一個 interceptor 中的生命週期有三步驟:1) Preprocessing、2) ActionInvocation.invoke() 與 3) Postprocessing。現在我們就遵循這三個步驟來撰寫我們自己的 interceptor。<br />以下的範例目的在於判斷使用者是否有登入本系統,如果沒有登入或是 session 中沒有使用者登入的紀錄就將頁面回到首頁。我們將整個 web application 規劃如下:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUuFw1zYz_1bIV5g7irjPVjIaqiyN2gULOkAdLdnd3GQNLGABvVv0extDScgScZRCmgycXgswVOITIHXf8svY9NWk1ioARC3yqx4FeFkysDeSW08MkuhCpDWpu4yAZR6y5FsRipmCo9vc/s1600-h/1.JPG"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 206px; height: 340px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUuFw1zYz_1bIV5g7irjPVjIaqiyN2gULOkAdLdnd3GQNLGABvVv0extDScgScZRCmgycXgswVOITIHXf8svY9NWk1ioARC3yqx4FeFkysDeSW08MkuhCpDWpu4yAZR6y5FsRipmCo9vc/s320/1.JPG" alt="" id="BLOGGER_PHOTO_ID_5321834168549080226" border="0" /></a>首先是 interceptor package 中的 MyInterceptor,這個 class 就是我們的主角,這個 interceptor 就是要負責檢查使用者是否有登入系統。<br />另外我規劃了一個 interface,就是 aware package 中的 PrivateAware interface,這個 interface 只是一個標記用的 interface,用來標記某個 action 是否需要經過 interceptor 的檢查。<br />在 bean package 中的 User class 就如同之前的範例一樣,用來儲存使用者的資料,因為目前不是 focus 在資料儲存,所以不會將這些資料儲存在資料庫中。<br />在 login package 中的兩個 class:LoginAction 與 LogoutAction 主要是用來執行登入與登出的 action。這兩個 actions 負責對 session 進行存取,並更新使用者是否有登入的狀態。<br />最後就是 info package 中的 UserInformationAction class,主要是負責顯示使用者登入後的資訊,這也是我們用來測試如果使用者沒有登入系統而想要執行 UserInformation action,系統會自動將網頁轉跳至登入的頁面。<br /><br />接下來我們就將這幾個部份深入探討。<br />首先我們先將我們整個範例所需要的程式先探討,接著才會探討我們今天的主角 MyInterceptor。一開始就是用來標記 action 是否需要進行檢查使用者登入狀態的 interface:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">interface</span> PrivateAware<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">static</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">final</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> USER_SESSION = <span style="color: rgb(42, 0, 255);">"user"</span>;<br />}<br /></pre><br />這個 interface 很簡單,沒有任何 method 需要實做,這就是為什麼他被稱為標記用的 interface。另外我們在 PrivateAware interface 中另外宣告了一個 constant USER_SESSION,這是用來儲存使用者資訊除存在 session 的字串。<br /><br />接著就是登入跟登出的 action,我們的登入登出 actions 都會將使用者資訊儲存到 session 中,所以這兩個 actions 都會 implements <a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/SessionAware.html">SessionAware</a>,這兩個 actions 的內容都很簡單我們就不贅述了:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> LoginAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport <span style="color: rgb(127, 0, 85); font-weight: bold;">implements</span> SessionAware<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> User user;<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> Map<<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>,<span style="color: rgb(127, 0, 85); font-weight: bold;">Object</span>> session;<br /><br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> User getUser()<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> user;<br />}<br /><br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setUser(User user)<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user = user;<br />}<br /><br />@Override<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> execute() <span style="color: rgb(127, 0, 85); font-weight: bold;">throws</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Exception</span><br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> result = SUCCESS;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">if</span> (<span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user.getName().equals(<span style="color: rgb(42, 0, 255);">"Silver"</span>) && <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user.getPassword().equals(<span style="color: rgb(42, 0, 255);">"hi"</span>))<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.session.put(PrivateAware.USER_SESSION, <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.user);<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">else</span><br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.session.remove(PrivateAware.USER_SESSION);<br /> result = LOGIN;<br /> }<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> result;<br />}<br /><br />@Override<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setSession(Map session)<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.session = session;<br />}<br /><br />}<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> LogoutAction <span style="color: rgb(127, 0, 85); font-weight: bold;">extends</span> ActionSupport <span style="color: rgb(127, 0, 85); font-weight: bold;">implements</span> SessionAware<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">private</span> Map<<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>, <span style="color: rgb(127, 0, 85); font-weight: bold;">Object</span>> session;<br /><br />@Override<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> execute() <span style="color: rgb(127, 0, 85); font-weight: bold;">throws</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Exception</span><br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.session.remove(PrivateAware.USER_SESSION);<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> LOGIN;<br />}<br /><br />@Override<br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> setSession(Map session)<br />{<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">this</span>.session = session;<br />}<br /><br />}<br /></pre><br />接下來就是我們的重頭戲了!我們自己設計的 interceptor 都需要 implements <a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/Interceptor.html">Interceptor</a> interface,並且要 implements intercept() method!以下就是我們的 MyInterceptor:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> MyInterceptor <span style="color: rgb(127, 0, 85); font-weight: bold;">implements</span> Interceptor<br />{<br /> @Override<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> destroy()<br /> {<br /> }<br /> @Override<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> init()<br /> {<br /> }<br /> @SuppressWarnings(<span style="color: rgb(42, 0, 255);">"unchecked"</span>)<br /> @Override<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> intercept(ActionInvocation actionInvocation) <span style="color: rgb(127, 0, 85); font-weight: bold;">throws</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Exception</span><br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">String</span> result = <span style="color: rgb(127, 0, 85); font-weight: bold;">null</span>;<br /> <span style="color: rgb(63, 127, 89);">//Proprocessing</span><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">if</span> (actionInvocation.getAction() <span style="color: rgb(127, 0, 85); font-weight: bold;">instanceof</span> PrivateAware)<br /> {<br /> Map<<span style="color: rgb(127, 0, 85); font-weight: bold;">String</span>, <span style="color: rgb(127, 0, 85); font-weight: bold;">Object</span>> session = actionInvocation.getInvocationContext().getSession();<br /> User user = (User) session.get(PrivateAware.USER_SESSION);<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">if</span> (user == <span style="color: rgb(127, 0, 85); font-weight: bold;">null</span>)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> Action.LOGIN;<br /> }<br /> }<br /> <span style="color: rgb(63, 127, 89);">//ActionInvocation.invoke()</span><br /> result = actionInvocation.invoke();<br /> <span style="color: rgb(63, 127, 89);">//Postprocessing</span><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> result;<br /> }<br /><br />}<br /></pre><br />我們在我們的 preprocessing 階段就檢查現在使用者所呼叫的 action 是否有 implements PrivateAware,如果有我們才會進行檢查的作業。接下來我們就會檢查 session 有沒有使用者登入過得狀態,也就是查詢 PrivateAware.USER_SESSION 字串。如果沒有就回傳 LOGIN 的控制字串給 Struts2 framework,而這個控制字串是定義在 struts.xml 中的 global-result,也就是整個在使設定檔之下的 components 都可以共用的!接這就是呼叫 actionInvocation.invoke() method,利用 recursive 的方式來走訪整個 interceptor stack,當我們在 postprocessing 時,我們已經無法更改使用者接下來會看到的頁面了!因為在呼叫 invoke() method 之後,使用者的下一個頁面就已經被決定了!所以我們的程式要在 preprocessing 中執行判斷使用者登入狀況,而不能在 postprocessing 中判斷!<br /><br />撰寫好 interceptor 之後,我們就要在設定檔中設定我們的 interceptor:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">package</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">default</span><span style="color: rgb(42, 0, 255);">"</span> extends=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">struts-default</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptors</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">authInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">silver8250.interceptor.MyInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-stack</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">authorizationStack</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">authInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">defaultStack</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptor-stack</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptors</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">default-interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">authorizationStack</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">global-results</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">result</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">login</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>/index.jsp<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">result</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">global-results</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(63, 127, 89);"><!--</span><span style="color: rgb(63, 127, 89);"> 設定 actions </span><span style="color: rgb(63, 127, 89);">--></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">package</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />首先我們要先透過 interceptors tag 來宣告我們自己的 interceptor stack,接著我們就將我們的 MyInterceptor 宣告,並且設定我們的 interceptor-stack tag,因為我們的 package 是繼承 struts-default,所以我們在 interceptor-stack 中可以 reuse defaultStack。最後我們就設定我們的 default-interceptor-ref tag,將預設的 interceptor stack 更改為我們剛剛設定的 interceptor stack!<br /><br />最後就是設定 global-result tag,當 interceptor 檢查到使用者沒有登入的話,就將頁面轉回到 index.jsp。<br /><br />而我們的 UserInformationAction class 就是一個 action 並且 implements PrivateAware interface,來告知 MyInterceptor 要檢查此 action 在執行前使用者是否有登入!<br /><br />在這裡我們簡單的介紹了如何撰寫我們自己的 interceptor,其實要撰寫這樣的 component 不難,也因為 interceptor 採用了 <a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming">AOP(aspect-oriented programming)</a> 的精神,讓我們撰寫的 interceptor 可以在 action 的生命週期中被 reuse!Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com4tag:blogger.com,1999:blog-6990680403271933461.post-69422613116056028222009-04-04T00:37:00.006+08:002009-05-06T23:56:33.755+08:00[JUnit] 如何測試一個 private method上次幫老師帶了一堂課後,那天同學有反應說關於測試的問題:要如何測試 private method 呢?這個問題好像有點難,不過如果我們利用 Java 提供的 reflection 機制就可以輕鬆的辦到!不過那也要會用才行囉!所以我就示範一下如何撰寫這樣的程式。<br />假設我們有一個程式如下:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> HavePrivateMethod<br />{<br /><span style="color: rgb(127, 0, 85); font-weight: bold;"> private</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> add(<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> x, <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> y)<br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> x + y;<br /> }<br />}<br /></pre><br />這個程式很簡單,可能你連用都不想用XD,不過因為是示範就別太計較了!反正就是有個 private method 為 add,parameter 與 return type 都是 int。以下就是我們的測試程式:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><br /><span style="color: rgb(127, 0, 85); font-weight: bold;">public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">class</span> HavePrivateMethodTest<br />{<br /> @Test<br /><span style="color: rgb(127, 0, 85); font-weight: bold;"> public</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">void</span> add() <span style="color: rgb(127, 0, 85); font-weight: bold;">throws</span> <span style="color: rgb(127, 0, 85); font-weight: bold;">Exception</span><br /> {<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;"> int</span> x = 1;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> y = 2;<br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">int</span> expectedResult = 3;<br /> <span style="color: rgb(63, 127, 89);"> /*</span><br /><span style="color: rgb(63, 127, 89);"> * 1.取得 instance</span><br /><span style="color: rgb(63, 127, 89);"> */</span><br /> HavePrivateMethod havePrivateMethodClass =<br /> HavePrivateMethod.class.newInstance();<br /> <span style="color: rgb(63, 127, 89);">/*</span><br /><span style="color: rgb(63, 127, 89);"> * 2.取得我們要測試的 method</span><br /><span style="color: rgb(63, 127, 89);"> */</span><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;"> Method</span> addMethod =<br /> havePrivateMethodClass.getClass().getDeclaredMethod(<br /> <span style="color: rgb(42, 0, 255);">"add"</span>,<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span>.class,<span style="color: rgb(127, 0, 85); font-weight: bold;">int</span>.class);<br /> <span style="color: rgb(63, 127, 89);"> /*</span><br /><span style="color: rgb(63, 127, 89);"> * 3.設定存取性</span><br /><span style="color: rgb(63, 127, 89);"> */</span><br /> addMethod.setAccessible(<span style="color: rgb(127, 0, 85); font-weight: bold;">true</span>);<br /> <span style="color: rgb(63, 127, 89);"> /*</span><br /><span style="color: rgb(63, 127, 89);"> * 4.實際呼叫</span><br /><span style="color: rgb(63, 127, 89);"> */</span><br /> <span style="color: rgb(127, 0, 85); font-weight: bold;">Object</span> actualResult =<br /> addMethod.invoke(havePrivateMethodClass, x, y);<br /> addMethod.setAccessible(<span style="color: rgb(127, 0, 85); font-weight: bold;">false</span>);<br /> Assert.assertEquals(expectedResult, actualResult);<br /> }<br />}<br /></pre><br />總之,我們要先取得一個該 class 的 instance,再來就是設定我們要測試的 private method,要取得此 method 的 <a href="http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html">Method</a> instance,由於我們測試的 private method 有兩個 primitive type 的 int,所以我們就用 int.class,而不是用 Integer.class 喔!因為這不一樣的~<br />取得 Method instance 後,我們就要先設定讓此 method 是可以被存取的,接下來才是呼叫(invoke) 此 method,呼叫後的回傳值一律都是 Object 型態,我們就透過 JUnit 提供的 assertEquals() method 來幫助我們檢查!<br />這樣就完成我們的目標囉~<br /><br /><span style="color: rgb(255, 0, 0);">NOTE:(2009-05-06)</span><br />在上面的範例程式中,我們採用 <span style="color: rgb(51, 51, 255);">HavePrivateMethod.class.newInstance();</span> 來初始化被測試的物件,不過這樣的初始化動作只限定用在被測的 class 具備有 default constructor!如果我們的被測試 class 沒有 default constructor 的話,我們就只能用一般的初始化方式,也就是 new Object 囉~這樣在 method reflection 也是會有效的!<br />感謝學弟大頭跟 Fred 提出這樣的問題~Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-1672463056891363952009-03-28T14:43:00.015+08:002009-03-28T18:54:18.966+08:00[Struts2] 宣告與設定 Interceptors我們已經知道 interceptor 是如何的運作以及 Struts2 framework 中已經幫我們建立了哪些的 interceptors。現在我們就要深入探討關於設定已經宣告我們的 interceptors!<br />在 Struts2 framework 中已經提供了 <a href="http://struts.apache.org/2.x/docs/struts-defaultxml.html">struts-default.xml</a> 幫助我們設定了一些內建的 interceptors 以及 interceptor stack,這讓我們減輕了不少的工作,不過我們在開發大型的系統時,這些預設的設定檔可能不太適用,所以我們還是要知道怎麼樣去自己設定 interceptors。<br /><br /><span style="font-weight: bold;font-size:100%;" >宣告與設定 interceptors</span><br />基本上來說,interceptor 的設定包含了兩個部份:interceptor 的宣告與 interceptor stack 的設定。我們就參考 <a href="http://struts.apache.org/2.x/docs/struts-defaultxml.html">struts-default.xml</a> 中的 interceptor:<br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">package</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">struts-default</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /><br />...<br /><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptors</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">alias</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.AliasInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">autowiring</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">chain</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.ChainingInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">conversionError</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.StrutsConversionErrorInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">createSession</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.CreateSessionInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">debugging</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.debugging.DebuggingInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">externalRef</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">execAndWait</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.ExecuteAndWaitInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">exception</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">fileUpload</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.FileUploadInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">i18n</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.I18nInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">logger</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.LoggingInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">modelDriven</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">scopedModelDriven</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">params</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.ParametersInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">prepare</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.PrepareInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">staticParams</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.StaticParametersInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">scope</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.ScopeInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">servletConfig</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.ServletConfigInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">sessionAutowiring</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">timer</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.TimerInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">token</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.TokenInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">tokenSession</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.TokenSessionStoreInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">validation</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">workflow</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">store</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.MessageStoreInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">checkbox</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.CheckboxInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">profiling</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.ProfilingActivationInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">roles</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.RolesInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">jsonValidation</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">org.apache.struts2.interceptor.validation.JSONValidationInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">annotationWorkflow</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /><br /> ...<br /><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-stack</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">defaultStack</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">exception</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">alias</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">servletConfig</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">prepare</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">i18n</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">chain</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">debugging</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">profiling</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">scopedModelDriven</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">modelDriven</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">fileUpload</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">checkbox</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">staticParams</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">params</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">param</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">excludeParams</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>dojo\..*<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">param</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">conversionError</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">validation</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">param</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">excludeMethods</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>input,back,cancel,browse<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">param</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">workflow</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">param</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">excludeMethods</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>input,back,cancel,browse<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">param</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptor-stack</span><span style="color: rgb(127, 0, 85);">></span><br /><br /> ...<br /><br /> <span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptors</span><span style="color: rgb(127, 0, 85);">></span><br /> <span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">default-interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">defaultStack</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">/></span><br /><br /> ...<br /><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">package</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><br />由上面的設定中我們可以看到,整個 interceptors 是宣告在 interceptor tag 之中,而且如同其他 framework components 一樣,這些設定都是屬於 package tag 之下!<br />在 interceptors tag 之中包含了兩組 tags:interceptor 與 interceptor-stack,我們可以從 tag 的名稱很容易瞭解其功能。<br />首先,我們透過 interceptor tag 來宣告我們擁有的 interceptors,當然,interceptors 要先被宣告才能使用,這就跟 programming language 一樣!而且宣告 interceptor 是很簡單的,我們透過 name attribute 給予該 interceptor 一個宣告的名稱,就像程式中宣告變數的名稱一樣;再來我們就是透過 class attribute 告知 framework,該 interceptor 所屬的 class 位置,這個位置一定要包含完整的 package name 與 class name(這裡說得是 class 位置,而不是檔案位置喔!所以最後面不需要加上 .java)!<br />宣告了 interceptors 之後,我們就可以設定我們的 interceptor stack,透過 interceptor-stack tag 來宣告一組 stack。這裡由於原始的 struts-default.xml 中的設定太過於龐大,所以我們只有顯示 defaultStack 的 interceptor stack。每一組 interceptor stack 我們都要給予一個唯一的 name attribute,而一個 interceptor stack 中都包含了一系列有順序性的 interceptor-ref tag,這個 tag 也是很容易瞭解的,interceptor-ref tag 就是用來參考(reference) 我們所宣告的 interceptors,所以我們在 interceptor-ref tag 中設定 name attribute 來參考到我們所宣告的 interceptors。<br />請注意剛才我所說得"有順序性",因為我們所宣告在 interceptor-stack tag 中的 interceptor ref 順序是在將來系統執行時由上而下的來執行!所以如果有某些順序被更動,可能會造成整個 interceptor stack 的不同。如果我們將 param interceptor 移到 workflow interceptor 之後,那我們就無法在 workflow interceptor 中使用param tag 來給予設定參數,因為 framework 對於 param tag 的執行是透過 param interceptor 來進行的,所以 param interceptor 要先被呼叫後,才能在之後的 interceptors 使用 param tag 進行參數設定!<br />當我們宣告完成 interceptors 以及設定好 interceptor stack 後,我們就要設定 framework 中預設使用的 interceptor stack 了!這個可以透過 default-interceptor-ref tag 來設定,我們透過給予 default-interceptor-ref tag 中的 name attribute 來告知 framework,我們要使用的 default interceptor stack!<br /><br /><span style="font-weight: bold;">針對每個 Action 設定特定的 interceptors</span><br />許多時候我們的 Action 都是繼承自其所屬 package 中對於 interceptors 的設定,不過也有時候我們可能會需要將某些 interceptors 用在某個 Action 呼叫之前,在 Struts2 framework 中提供了一個很方便的設定方式,讓我們可以為某一個 Action 給予特定的 interceptors,讓我們看看下面的設定:<br /><pre style='color:#000000;background:#ffffff;'><span style='color:#7f0055; '><</span><span style='color:#7f0055; '>action</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>MyAction</span><span style='color:#2a00ff; '>"</span> class=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>silver8250.test.MyAction</span><span style='color:#2a00ff; '>"</span><span style='color:#7f0055; '>></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>interceptor-ref</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>timer</span><span style='color:#2a00ff; '>"</span> <span style='color:#7f0055; '>/></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>interceptor-ref</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>logger</span><span style='color:#2a00ff; '>"</span> <span style='color:#7f0055; '>/></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>result</span><span style='color:#7f0055; '>></span>/WEB-INF/pages/finish.jsp<span style='color:#7f0055; '></</span><span style='color:#7f0055; '>result</span><span style='color:#7f0055; '>></span><br /><span style='color:#7f0055; '></</span><span style='color:#7f0055; '>action</span><span style='color:#7f0055; '>></span><br /></pre><br />這個設定中告知 Struts2 framework,我們有一個 MyAction 的 Action,要使用 timer 與 logger interceptor,而且 interceptor 呼叫的順序是依照我們的排列,不過我們的 MyAction 是不會有其他的 interceptors 了!這點要注意,如果我們採用 Action 中自己定義的 interceptors,那我們就會失去原先在設定檔中設定的 interceptor stack 了!不過為了讓我們可以很方便的 reuse 原先我們設定的 interceptor stack,我們可以直接在 interceptor-ref tag 中設定某個已經存在的 interceptor stack,如下:<br />這種設定方式讓我們可以很方便的 reuse 某個 interceptor stack,當然,我們可以安排不一樣的順序!<br /><pre style='color:#000000;background:#ffffff;'><span style='color:#7f0055; '><</span><span style='color:#7f0055; '>action</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>MyAction</span><span style='color:#2a00ff; '>"</span> class=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>silver8250.test.MyAction</span><span style='color:#2a00ff; '>"</span><span style='color:#7f0055; '>></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>interceptor-ref</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>timer</span><span style='color:#2a00ff; '>"</span> <span style='color:#7f0055; '>/></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>interceptor-ref</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>logger</span><span style='color:#2a00ff; '>"</span> <span style='color:#7f0055; '>/></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>interceptor-ref</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>defaultStack</span><span style='color:#2a00ff; '>"</span> <span style='color:#7f0055; '>/></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>result</span><span style='color:#7f0055; '>></span>/WEB-INF/pages/finish.jsp<span style='color:#7f0055; '></</span><span style='color:#7f0055; '>result</span><span style='color:#7f0055; '>></span><br /><span style='color:#7f0055; '></</span><span style='color:#7f0055; '>action</span><span style='color:#7f0055; '>></span><br /></pre><br /><span style="font-weight: bold;">Action 中 Overriding interceptor 的參數</span><br />上面的方法是為某個 Action 設定不同的 interceptor stack,現在我們則是要修改 Action 的 interceptor stack 中的某個參數,先看看下面的設定:<br /><br />在這裡的 MyAction 我們一樣給予一個我們自己設定的 interceptor stack,只不過是 reuse defaultStack,這樣的方式讓我們可以調整我們 reuse 的 interceptor stack,就如同上面所設定的:透過 param tag 告知 framework,目前 MyAction 是要使用 defaultStack 中定義的 interceptor stack,不過對於 workflow interceptor,我們要設定 excludeMethods 改為 someMethod 來取代原先設定的參數!<br />這種方式讓我們可以為某個 Action 調整特定的 interceptor stack,當然,我們也可以將這種方法結合上面的方法來完成更多不同的 interceptor stack。<br /><pre style='color:#000000;background:#ffffff;'><span style='color:#7f0055; '><</span><span style='color:#7f0055; '>action</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>MyAction</span><span style='color:#2a00ff; '>"</span> class=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>silver8250.test.MyAction</span><span style='color:#2a00ff; '>"</span><span style='color:#7f0055; '>></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>interceptor-ref</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>defaultStack</span><span style='color:#2a00ff; '>"</span><span style='color:#7f0055; '>></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>param</span> name=<span style='color:#2a00ff; '>"</span><span style='color:#2a00ff; '>workflow.excludeMethods</span><span style='color:#2a00ff; '>"</span><span style='color:#7f0055; '>></span>someMethod<span style='color:#7f0055; '></</span><span style='color:#7f0055; '>param</span><span style='color:#7f0055; '>></span><br /> <span style='color:#7f0055; '></</span><span style='color:#7f0055; '>interceptor-ref</span><span style='color:#7f0055; '>></span><br /> <span style='color:#7f0055; '><</span><span style='color:#7f0055; '>result</span><span style='color:#7f0055; '>></span>/WEB-INF/pages/finish.jsp<span style='color:#7f0055; '></</span><span style='color:#7f0055; '>result</span><span style='color:#7f0055; '>></span><br /><span style='color:#7f0055; '></</span><span style='color:#7f0055; '>action</span><span style='color:#7f0055; '>></span><br /></pre><br />在這篇中我們深入了描述了 interceptor 的設定與宣告,我們可以為一整個 package 中的 Actions 設定共用的 interceptor stack,也可以特定位某個 Action 設定特別的 interceptor stack,也可以 reuse 某個 interceptor stack 並且調整不同的設定參數,甚至我們可以結合這兩種方式來提供更好用的 interceptor stack!Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-12342893548204175222009-03-19T22:24:00.004+08:002009-03-21T23:13:03.382+08:00[Struts2] 內建的 Interceptors<p>在 Struts2 framework 中,擁有許多的內建 Interceptors,這讓 programmers 可以很方便的 reuse,甚至我們根本就不需要開發自己的 interceptors,因為內建的 interceptors 已經做了大部分的工作!這種好處不僅僅在 interceptor 中才有,其他的 Struts2 components 也有這樣的特性:framework 已經內建好很多很好用的 components,讓我們很專心的在開發我們的 business logic。</p> <p>要知道 Struts2 中內建有哪些的 interceptors,我們可以從 <a href="http://struts.apache.org/2.1.6/docs/struts-defaultxml.html">struts-default.xml</a> 中得到資訊!以下我只會簡單的介紹幾個比較重要的 interceptors,剩下的就留給有興趣的你來 survey。</p> <p><strong style="font-size: 18px;">1. Utility Interceptors</strong></p> <p>首先,我們先看看一些工具類的 interceptors,這類的 interceptors 主要是幫助 programmer 來開發、調校與找 bug 所使用的。</p> <ul> <li><a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/TimerInterceptor.html">Timer interceptor</a></li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>這個 interceptor 很直觀就是用來測時間的!這個 interceptor 可以幫助 programmer 測量每一次 request 執行所花費的時間,而且很有趣的是,如果我們將這個 interceptor 放在 stack 的最上層,我們測量的時間就是整個 request 執行的時間;如果我們將 timer interceptor 放在 stack 的最下層,那我們量測的時間就會是 Action 執行的時間!</p> <p>所以我們可以藉由調整這個 interceptor 的位置來測量 request 的時間或是 Action 執行時間。</p> </blockquote> <ul> <li><a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/LoggingInterceptor.html">Logging interceptor</a></li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>這個 interceptor 提供一個很簡單的 logging 機制,此 interceptor 會在 preprocessing 與 postprocessing 進行 logging 的動作。這樣可以很容易的 debug,因為我可以追蹤使用者的使用紀錄,進而找出問題的所在。</p> </blockquote> <p><span style="font-size:18;"><strong>2. Data Transfer Interceptors</strong></span></p> <p>資料轉換是一個從 client 到 server 端很重要的角色,因為使用者填寫完畫面上的表單後,接著就會透過 interceptors 將資料 mapping 到 server 端的 java 物件。也就是將 request parameter 中的資料移動到 Action 之中。</p> <ul> <li><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/ParametersInterceptor.htmlrceptor/ParametersInterceptor.html">Params interceptor</a>(default stack)</li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>這是一個很重要的 interceptor,這個 interceptor 會主要將 request parameters 轉換到 ValueStack 中相對映的 properties。這個 interceptor 也會與 OGNL 一起合作將 parameter 作轉換,並且可以幫助我們將 parameter 的值轉換到 ModelDriven 的 Action 中。不過,param interceptor 並不會知道資料確實是要存放在哪,他只會將 parameter 的值轉換到第一個相符的 property 中。這點一開始會令人感到很詭異,不過因為現在還不會對 ValueStack 作很深入的探討,所以你就先記著這個特性,之後我們在深入的討論 ValueStack。</p> </blockquote> <ul> <li><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.html">Static-params interceptor</a>(default stack)</li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>這個 interceptor 跟上面的 param interceptor 功能很像,都是將 parameter 中的值轉換到 ValueStack 中,不過不一樣的是,這個是將 configuration file 中設定的 parameter 轉換到 ValueStack 中:<br /></p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">action</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">myAction</span><span style="color: rgb(42, 0, 255);">"</span> class=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">silver8250.my.MyAction</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">param</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">firstName</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>Silver<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">param</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">param</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">lastName</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>Su<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">param</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">action</span><span style="color: rgb(127, 0, 85);">></span><br /></pre>從上面的 configuration 中可以得知,我們可以在 configuration 中先設定預設 parameter 的值,static-param interceptor 會幫我們將這些設定在 configuration 中的 parameter 轉換到 Action 中。有一點值得注意的是,因為 static-param interceptor 在 stack 中是排在 param interceptor 之前,那就代表著如果使用者的表單中也有 firstName 與 lastName 的話,當表單送交後,就會覆蓋原先在 configuration 中設定的值。這樣可以讓使用者沒有填的欄位具有 default value。 <ul dir="ltr"> <li><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/ServletConfigInterceptor.html">Servlet-config interceptor</a>(default stack)</li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p dir="ltr">這個 interceptor 也是相當重要的一個,因為他提供了 <a href="http://en.wikipedia.org/wiki/Dependency_injection">Dependency Injection(DI)</a> 的功能。DI 簡單來說就是我們不要直接的去要求某些物件,反而是讓 framework 自己給我某些物件!如果你有熟悉 <a href="http://www.springsource.org/">Spring framework</a> 的話,DI 對你來說應該就是家常便飯了!</p> <p dir="ltr">如果你有寫過 Struts 1.x,我想以下的例子你就可以更深刻的體會了!在 Struts 1.x 中,execute() method 都會與 <a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/http/HttpServletRequest.html">HttpServletRequest</a> 等物件有相依性,這樣就造成了測試上的不方便,因為我們要將這些 Container 物件傳遞給 execute() method,我們才能進行測試的工作!也就是我們要使用 Mock Object 來測試,這樣讓測試的工作不單純。不過請你回想一下在 Strut2 中我們的 Action execute() method 是很 pure 的,我們無須讓呼叫者傳遞任何的 parameters 給我們!但是你應該會跟我一樣很 confuse!我們要怎要拿到 HttpServletRequest 物件呢?別急!接下來就是 Servlet-config interceptor 的能力要發揮了~</p> <p dir="ltr">Servelt-config interceptor 提供了以下的 interfaces 讓 programmer 可以 implement:</p> <ol dir="ltr"> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/util/ServletContextAware.html">ServletContextAware</a> - 設定 ServletContext 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/ServletRequestAware.html">ServletRequestAware</a> - 設定 HttpServletRequest 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/ServletResponseAware.html">ServletResponseAware</a> - 設定 HttpServletResponse 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/ParameterAware.html">ParameterAware</a> - 設定 request parameter 的 Map 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/RequestAware.html">RequestAware</a> - 設定 request 的 Map 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/SessionAware.html">SessionAware</a> - 設定 session 的 Map 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/ApplicationAware.html">ApplicationAware</a> - 設定 application 的 Map 物件給 Action</div> </li> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/PrincipalAware.html">PrincipalAware</a> - 設定 Principal 物件給 Action,用於 security</div> </li> </ol> <p dir="ltr">在上面所列出的 8 項中,最常用的應該就是前 7 個,而且這每一個 interface 都只有一個 setter method(如:SessionAware 的 setSession(Map<String, Object> session) method)。第 1~3 的 interfaces 屬於將原始的 JSP 物件設定給 Action,而 4~7 是 Struts2 framework 將各個在 JSP 的隱含物件(Implicit Object)提供 Map 方式存取!</p> <p dir="ltr">只要我們的 Action implement SessionAware 並且實做出 setSession() method,我們就可以在 Action 中存取 Session Map 物件,如下:<br /></p><br /><pre style='color:#000000;background:#ffffff;'><br /><br /><span style='color:#7f0055; font-weight:bold; '>public</span> MyAction <span style='color:#7f0055; font-weight:bold; '>implements</span> SessionAware<br />{<br /> <span style='color:#7f0055; font-weight:bold; '>private</span> Map<<span style='color:#7f0055; font-weight:bold; '>String</span>, <span style='color:#7f0055; font-weight:bold; '>Object</span>> session;<br /> <span style='color:#7f0055; font-weight:bold; '>public</span> setSession(Map<<span style='color:#7f0055; font-weight:bold; '>String</span>, <span style='color:#7f0055; font-weight:bold; '>Object</span>> session)<br /> {<br /> <span style='color:#7f0055; font-weight:bold; '>this</span>.session = session;<br /> }<br /> <span style='color:#7f0055; font-weight:bold; '>public</span> <span style='color:#7f0055; font-weight:bold; '>String</span> execute()<br /> {<br /> <span style='color:#3f7f59; '>//對 session 物件進行存取!</span><br /> }<br />}<br /></pre><br /><p dir="ltr">上面的程式中我們先宣告一個 session 的 Map 物件,這是為了方便我們在 execute() method 中存取 session,所以我們透過 setSession() method 將 framework 給我們的 session Map 物件設定到我們的 session 變數。其他的 interface 用法也如同 SessionAware 的使用方式。</p> <ul dir="ltr"> <li><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/FileUploadInterceptor.html">Fileupload interceptor</a></li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p dir="ltr">這個 interceptor 幫我們將 client 端 form 的 multipart request 轉換成 Action 中的物件,不過這轉換的機制跟 param interceptor 有點不一樣,因為在 defaultStack 中,Fileupload interceptor 是被安排在 param interceptor 之前,所以,Fileupload 是將 parameter 的值轉換成像是一般的 parameter 一樣(有興趣的你可以自行研究一下 API 的內容~),然後在交給 param interceptor 去處理,而這樣的處理就變得更單純了!</p> </blockquote> <p dir="ltr"><strong style="font-size: 18px;">3. Workflow Interceptors</strong></p> <p dir="ltr"><span style="font-size:1em;">Workflo</span>w 類型的 interceptors 提供了許多在執行 Action 之前的最後作業,所以 workflow 類型的 interceptors 通常是在 stack 中的最後面幾個,因為當所有的 data 都完成轉換後,workflow 就會開始進行一連串的流程作業。</p> <p dir="ltr">而這裡所謂的 workflow 是指:一系列的流程,從 interceptor 開始到 Action 執行,產生 Result 最後再回到 interceptor 的過程。而 workflow interceptors 扮演著檢驗流程中處理的狀態,如果流程中有任何狀態是有問題的,就會進行非正常的流程處理。</p> <ul dir="ltr"> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/index.html">Workflow interceptor</a>(default stack)<br />別懷疑,workflow 確實是一個 interceptor!這個 interceptor 會負責 data validation,並且會將驗證錯誤的資訊傳送到 client 端的頁面。這裡所謂的 data validation 就是我們之前在<a href="http://silver8250.blogspot.com/2009/02/struts2-action.html">深入實做 Action</a> 中提到的 basic validation,也就是我們在 Action 中撰寫的 validate() method。下面的程式碼就是簡化過得 workflow interceptor:(雖然簡化,不過功能很齊全!)</div> </li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"><br /><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> String intercept(ActionInvocation invocation) throw Exception<br />{<br />Action action = invocation.getAction();<br /><span style="font-weight: bold; color: rgb(127, 0, 85);">if</span> (action <span style="font-weight: bold; color: rgb(127, 0, 85);">instanceof</span> Validateable)<br />{<br /> Validateable validateable = (Validateable) action;<br /> validateable.validate();<br />}<br /><span style="font-weight: bold; color: rgb(127, 0, 85);">if</span> (action <span style="font-weight: bold; color: rgb(127, 0, 85);">instanceof</span> ValidationAware)<br />{<br /> ValidationAware validationAwareAction =<br /> (ValidationAware) action;<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">if</span> (validationAwareAction.hasErrors())<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> Action.INPUT;<br /> }<br />}<br /><span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> invocation.invoke();<br />}<br /></pre><br /><p>如果我們的 Action 是 extends ActionSupport 的話,ActionSupport 就會幫我們 implement Validateable 與 ValidationAware interfaces。第一個 if statement 用來判斷此 Action 是否為一個 Validateable 物件,並且執行 validate() method。第二個 if statement 則是在判斷是否有 Error message 被儲存在 ValidationAware 中。</p> <p>另一個有趣的功能就是 workflow 可以透過 param interceptor 被調整,怎麼說呢!在 struts-default.xml 中我們可以看到 workflow interceptor 已經被調整成如下:</p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">workflow</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">param</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">excludeMethods</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>input,back,cancel,browse<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">param</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">interceptor-ref</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><p>上面的設定中我們告知 workflow interceptor:當執行 validate 時,請忽略 input, back, cancel, browse 為 Action 進入點的 methods。關於目前我們只有使用最簡單的 Action,也就是單一的 execute() method 為進入點的 Action。所以目前你可能會沒有感覺,但是我想表達的是關於 workflow interceptor 是可以被這樣用來調整成適合我們的!以下就列出一些 workflow 准許我們調整的 parameters。</p> <table border="1" width="400"> <tbody> <tr> <td>參數</td> <td>值</td> <td>預設值</td> <td>說明</td> </tr> <tr> <td>alwaysInvokeValidate</td> <td>true / false</td> <td>true</td> <td> <p>設定是否總是呼叫 validate() method</p> </td> </tr> <tr> <td>inputResultName</td> <td>String</td> <td>Action.INPUT</td> <td>設定如果驗證失敗則要產生的頁面</td> </tr> <tr> <td>excludeMethods</td> <td>String</td> <td>N/A</td> <td>設定不執行驗證的 Action 進入點 method</td> </tr> </tbody> </table> </blockquote> <ul dir="ltr"> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/validator/ValidationInterceptor.html">Validation interceptor</a>(default stack)</div> </li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>上面的 workflow interceptor 已經提供了驗證的功能了!那 Validation interceptor 要用來做什麼呢?你應該會很困惑~雖然 workflow interceptor 已經做了最基本的驗證功能,不過對於一個大型的 web application 來說,要將類似的驗證邏輯都重複寫在不同的 Action validate() method 中其實很不方便!所以 Validation interceptor 就是用來解決這樣的問題,在 Struts2 中提供了另一種 Validation framework,讓 programmer 可以將驗證邏輯撰寫一次,然後藉由設定檔在不同的 Action 進行相同的驗證!這個機制的好處對於大型的 web application 來說是比較好得!畢竟複製一堆相同程式碼在不同的地方,對於日後的維護是相當累的!</p> <p>我們在這裡並不會詳談關於 Validation framework 的部份,不過可以簡單的介紹一下。當 programmer 將各個 Action 的驗證規則定義好後,validation interceptor 就會根據設定檔進行驗證的工作,當有發現驗證失敗的地方,會將錯誤訊息儲存在 ValidationAware 中,所以在 workflow interceptor 中還是需要將這些錯誤訊息顯示在 client 端頁面上!不過我們還是可以撰寫 validation() method,這個 method 的內容還是會被執行的!</p> <p>另外,在 interceptor stack 中,validation interceptor 一定要在 workflow 之前,否則驗證有錯誤的訊息就不會被顯示了!</p> </blockquote> <ul dir="ltr"> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/PrepareInterceptor.html">Prepare interceptor</a>(default stack)</div> </li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>Prepare interceptor 提供了一種方式讓我們可以介入整個 request 流程,也就是如果我們的 Action implements <a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/Preparable.html">Preparable</a> interface 就可以擁有這樣的好處(P.S. ActionSupport 沒有 implement Preparable),並且實做 prepare() method 就可以!</p> <p>當 Prepare interceptor 執行時會先檢查該 Action 是否為一個 Preparable,如果是,就會呼叫 prepare() method。所以我們可以在 Action 被執行之前(更嚴格的說是在 Prepare interceptor 之後的動作執行前),將前置動作寫在 prepare() method 中。</p> <p>Prepare interceptor 也提供一個 parameter 可以調校:</p> <p>alwaysInvokePrepare - 是否總是呼叫 prepare() method,預設是 true。</p> </blockquote> <ul dir="ltr"> <li> <div><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptor.html">ModelDriven interceptor</a>(default stack)</div> </li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p>這個 interceptor 對我們來說並不陌生,很直觀的,他就是用來處理 ModelDriven 的 Action。ModelDriven interceptor 執行時,會先判斷此 Action 是否有 implements ModelDriven interface,接著就會將 getModel() method 中的 model 儲存到 ValueStack 中。</p> <p>需要注意的是,ModelDriven interceptor 一定要安排在 static-parameter 與 parameter interceptor 之前,否則將不會發揮功能!</p> </blockquote> <p style="margin-right: 0px;" dir="ltr"><strong style="font-size: 18px;">4. Miscellaneous Interceptors</strong></p> <p style="margin-right: 0px;" dir="ltr"><span style="font-size:1em;">這類的 interceptors 是屬於一些輔助型的,而且這些 interceptors 對整個 stack 的其他 interceptors 進行點綴。</span></p> <ul dir="ltr"> <li> <div style="margin-right: 0px;"><span style="font-size:1em;"><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/ExceptionMappingInterceptor.html"><span style="font-size:1em;">Exception interceptor</span></a> (default stack)</span></div> </li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p style="margin-right: 0px;"><span style="font-size:1em;">這個 interceptor 很重要,因為他會負責處理整個 request 流程中的 exception。在 defaultStack 中 exception interceptor 是第一個被呼叫的 interceptor,並且會擷取使用者定義的 exception,將訊息顯示在使用者定義的 exception page。也因為最上層的 interceptor 所以他會擷取所有的 exception,若是放在最下層,則只會擷取到 Action 所產生的 exception。</span><br /></p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">global-results</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">result</span> name=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">error</span><span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(127, 0, 85);">></span>/WEB-INF/pages/error.jsp<span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">result</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">global-results</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">global-exception-mappings</span><span style="color: rgb(127, 0, 85);">></span><br /><span style="color: rgb(127, 0, 85);"><</span><span style="color: rgb(127, 0, 85);">exception-mapping</span> exception=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">java.lang.Exception</span><span style="color: rgb(42, 0, 255);">"</span> result=<span style="color: rgb(42, 0, 255);">"</span><span style="color: rgb(42, 0, 255);">error</span><span style="color: rgb(42, 0, 255);">"</span> <span style="color: rgb(127, 0, 85);">/></span><br /><span style="color: rgb(127, 0, 85);"></</span><span style="color: rgb(127, 0, 85);">global-exception-mappings</span><span style="color: rgb(127, 0, 85);">></span><br /></pre><p style="margin-right: 0px;"><span style="font-size:1em;">上面的設定是屬於 configuration file中,global-results tag 表示當某個 Action 所回傳的控制字串不再 Action 的定義內,framework 會去搜尋 global-results。也就是說 global-results 是在這個 configuration file 中可以共用的!另外,global-exception-mapping tag 就是定義 exception interceptor 要幫我們處理哪些 exception,並且要將錯誤的訊息顯示在哪個頁面!</span></p> <p style="margin-right: 0px;"> </p> </blockquote> <ul dir="ltr"> <li> <div style="margin-right: 0px;"><span style="font-size:1em;"><a href="http://struts.apache.org/2.0.14/struts2-core/apidocs/org/apache/struts2/interceptor/ScopeInterceptor.html"><span style="font-size:1em;">Scoped-ModelDriven interceptor</span></a> (default stack)</span></div> </li> </ul> <blockquote style="margin-right: 0px;" dir="ltr"> <p style="margin-right: 0px;"><span style="font-size:1em;">這個 interceptor 是可以讓我們將 ModelDriven 所使用的 model object 儲存在 session 等等的 scope,不過目前我不會詳細的介紹!</span></p> </blockquote> <p style="margin-right: 0px;" dir="ltr"><span style="font-size:1em;">在這裡我們介紹了很多有用的 interceptors,有興趣的你可以各自深入的瞭解,在這裡我只能很簡略的討論各個 interceptors 的功能而已!</span></p> </blockquote> </blockquote>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-83207952585671239952009-03-15T19:32:00.001+08:002009-03-15T19:58:29.300+08:00[Struts2] 深入探討 Interceptor<p>先前我們已經簡單的介紹了 interceptor 的功能以及整個 interceptor stack 與 Action 呼叫之間的順序!現在我們要將我們的焦點往 interceptor 拉近一些,也就是我們要看看每一個 interceptor 內部的實做面,不過在這裡我們並不會開發我們自己的 interceptor!</p> <p>在 <a href="http://silver8250.blogspot.com/2009/03/struts2-introduce-interceptor.html">Introduce Interceptor</a> 中我們有提到 <a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/ActionInvocation.html">ActionInvocation</a> 物件,這個物件扮演著整個 interceptor stack 最重要的核心角色!接下來我將會分成兩個 section 來探討我們的主題:1) ActionInvocation guy 與 2) Interceptor 的流程。</p> <p><strong>ActionInvocation guy</strong></p> <p>我們之前說過,ActionInvocation 是整個 interceptor stack 中最重要的核心角色,不過當我們瞭解 ActionInvocation 物件時,並不代表我們就會瞭解整個 Struts2 framework 對於處理 request 的流程!當我們在開發 interceptor 時,我們並不會直接的與 ActionInvocation 物件互動,而是間接的與 ActionInvocation 有關係!</p> <p>ActionInvocation 是一個包裝了所有與 Action 執行有關的 detail information 的物件,當我們的 framework 接收到一個使用者的 request 時,Struts2 framework 會根據使用者呼叫的 URL 來 mapping 所對應的 Action,並且會將此 Action 的相關資訊加入到 ActionInvocation 物件中,接著 Struts2 framework 就會搜尋所有 configuration file 中的所有 interceptors 並且加到 ActionInvocation 中,幫助 ActionInvocation 走訪所有的 interceptors。所以,ActionInvocation 會紀錄整個 request processing 的重要資訊,並且當整個 Struts2 framework 在運作時,ActionInvocation 物件會決定哪一個 component 將會是下一個被呼叫,以及該 component 被呼叫時要給予哪些 data 等等的工作!</p> <p><strong>Interceptor 的流程</strong></p> <p>介紹完整個 interceptor stack 中的核心角色後,我們接著就要來看看單一 interceptor 內部執行的流程!當系統中的 ActionInvocation 物件被 setup 完成後,接著就會依照 configuration file 中設定的 interceptor 順序來執行所有的 interceptors。然而在 ActionInvocation 中最重要的 operator 就是 <a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/ActionInvocation.html#invoke()">invoke() method</a>,這個 method 會在 ActionInvocation 被 Struts2 framework 設定完成後第一個呼叫的 method,因為 invoke() method 中會具備有執行 interceptor stack 中所有 interceptors 的功能。所以當 invoke() method 第一次被呼叫時,ActionInvocation 就會執行 stack 中第一個 interceptor。值得注意的是,invoke() method 第一的執行並不匯總是執行第一個 interceptor,執行哪一個 interceptor 與傳送哪些資料最終還是由 ActionInvocation 決定,當我們的 interceptor 要被執行時,ActionInvocation 就會呼叫 interceptor 中的 <a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/interceptor/Interceptor.html#intercept(com.opensymphony.xwork2.ActionInvocation)">intercept() method</a>。</p> <p>當每一次 invoke() method 被呼叫時,ActionInvocation 會決定接下來是哪一個 interceptor 要被執行,然後呼叫該 interceptor 的 intercept() method。這樣的流程會被執行道沒有下一個 interceptor 要被執行時,ActionInvocation 就會改呼叫 Action 的 execute() method 來執行真正的 business logic,再來 ActionInvocation 就會依照相反地順序呼叫剛剛的 interceptors!</p> <p>不知道你是否有看出來?這樣的 interceptor stack 的呼叫是一種 recursive 的方是在執行。當前一個 interceptor 呼叫 invoke() method 後,就會執行當前 interceptor 的 preprocessing 程式,接著就要再呼叫 invoke() method,讓 ActionInvocation 執行下一個 interceptor 的 preprocessing,當所有 interceptors 的 proprocessing 完成後才是 Action 的執行,最後在反向的執行每個 interceptor 的 postprocessing。由圖一我們可以看出一個 interceptor 的執行週期。</p> <p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz0ylVPMFV-15qNW8cjviVAcNJAbZcwNHYjhcum2bY0b9EmzFrbtnSOz9aD4THoVigSYkOM-LQYjhOau6IUdZCc2zkRsFzsbnFgR9MA-3_gFa6SAaZof7YeSI76KFUXhxCLKxHgPdiCTU/?imgmax=512" height="270" width="426"/></p> <p>圖一 Interceptor 的執行週期</p> <p>讓我們整理一下單一的 interceptor 在撰寫程式時的結構:</p> <ol> <li>執行 Preprocessing,在這個階段我們可以準備一些在 Action 執行前的作業,我們可以 filter data, alert message 或是處理重要的 data。</li> <li>將控制權轉交給下一個 interceptor,而最終是交給 Action 的 execute() method,所以我們呼叫 ActionInvocation.invoke()。在呼叫後,invoke() method 會回傳一個 control string,這個 control string 會由下一個 interceptor 傳遞過來,如果整個執行流程中沒有任何的失誤,control string 將會是我們之前在 Action 的 execute() method 所回傳的那個 string。</li> <li>執行 Postprocessing,當我們接收到 control string 後,我們就可以開始執行 postprocessing 的動作,在這裡可以做一些資料的後置處理如:logging 等。不過必須注意的是:當我們獲得 control string 後,代表著回傳給使用者的頁面已經被確定了!所以我們更改 control string 的內容並不會造成頁面的不同!也就是說,當我們需要對 control string 做改變時,我們必須在 preprocessing 階段就動手,這樣才會算數!</li> </ol> <p>在這裡我們比較深入的討論到 interceptor 內部的動作,接下來我們才會著手進行我們自己的 interceptor。</p>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-71373444107920040962009-03-15T19:31:00.001+08:002009-03-15T19:56:47.138+08:00[Struts2] Introduce Interceptor<p>之前我們在 <a href="http://silver8250.blogspot.com/2009/01/struts2-how-struts2-works.html">How Struts2 works</a> 中有稍微的談到 Interceptor。在 Struts2 中 Interceptor 算是與 Struts1 不同處的最大突破!因為 Interceptor 幫助我們可以很容易的 reuse components 的功能。而且 Interceptor 在 Struts2 framework 中扮演著很重要的關鍵角色,因為 Interceptor 的導入,讓 Struts2 framework 更達到 <a href="http://en.wikipedia.org/wiki/Separation_of_concerns">Separation of concerns</a>。(也就是專門的 component 就做自己專門的工作,無須與其他 components 有太過複雜的互動等)</p> <p><a href="http://en.wikipedia.org/wiki/Separation_of_concerns">Separation of concerns</a> 讓系統可以更清楚的劃分各個區塊的工作,在 Struts2 framework 中主要 focus 的 concern 就是 MVC,所以 <a href="http://en.wikipedia.org/wiki/Separation_of_concerns">Separation of concerns</a> 讓 Struts2 的 MVC 架構更乾淨明瞭!而且從架構的觀點來說,interceptor 讓我們可以更專注的在開發我們的 web application,讓一些每個部份都需要用到的功能(例如:Logging 等)放在 interceptor 中給大家共用,而我們的 business logic 並不會受到任何的影響。換句話說,當我們在開發我們的 Actions 時,我們不用自己在 Action 中加入 Logging 的程式碼,透過 interceptor 幫我們做 logging 的工作,並且所有的 Actions 都可以享有這樣的功能!這種就是 <a href="http://en.wikipedia.org/wiki/Cross-cutting_concern">cross-cutting concern</a>。</p> <p>在 <a href="http://silver8250.blogspot.com/2009/01/struts2-how-struts2-works.html">How Struts2 works</a> 中我們有提到:Interceptor stack 是在整個流程中第一個被執行,也是最後一個被執行的!這就表示說 interceptor stack 中蒐集了很多的 interceptors,而這些 interceptors 都可以在執行我們的 Action 之前做一些 preprocessing 與 postprocessing 的工作!一個最直觀的 preprocessing 工作就是 data transfer,還記得使用者在 JSP 頁面中可以輸入資料,而這些資料最後都會被 mapping 到 Action 的 properties,這樣的工作就是交由 param interceptor 幫我們完成的!而這工作是必須在我們的 Action 執行前就要先被系統完成的前置工作,因為我們的 Action 在執行 execute() method 時,通常都需要使用者輸入的資料。而 postprocessing 就以 Logging 為例,我們可能希望系統執行過哪些 Actions 的紀錄要做 Log 以方便日後追查用等等,所以就需要在 Action 執行完之後進行 Logging 的作業。</p> <p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi5_uURTBA56KveLZM9kU3wB6ctLfS1X0m3MJqQUtliMdTLJ7UN-ohiZCDCnNiPT_Neiv7gLQCB9YsOvIPEDhUffk-FsghbB-oFzLduajM_9pwsceR18Kth2QnEocBeiCV6V2JlNAeasY/?imgmax=512" height="424" width="430"/></p> <p style="TEXT-ALIGN: left">圖一 ActionInvocation</p> <p style="TEXT-ALIGN: left">圖一中表示,當 Struts2 framework 呼叫使用者所 request 的 Action 前,會先呼叫該 Action 所屬的 configuration file(.xml) 中 interceptor stack,這裡會定義一些再呼叫 Action 之前的動作,這些動作與 Action 是沒有直接關聯,但是卻是讓撰寫 Action 的 programmer 可以不用煩惱 detail 的動作。從 interceptor stack 為 stack 就知道,這樣的呼叫過程式 Layered process 呼叫方式,而 stack 的精神就是 FILO(First-In-Last-Out),所以在 interceptor stack 中的第一個 interceptor 會被第一個呼叫,也會是最後一個呼叫。如圖一中的第一個 interceptor 為 exception interceptor,在 Action 執行會是第一個被執行的 interceptor,當 Action 執行完後,Struts2 安排好顯示給使用者的Result 畫面,接著才會在回到 interceptor stack 中依照剛剛相反地順序執行 interceptors,所以這時候 exception interceptor 就會變成整個流程中最後一個被執行的 interceptor。如果我們的 interceptor 在 stack 中是最後一個的話,我們不必直接的呼叫 Action 的 execute() method,因為 Struts2 framework 會產生一個 <a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/ActionInvocation.html">ActionInvocation</a> object 物件負責協調各個 interceptors 之間以及與 Action 之間的互動,甚至我們無須知道我們的 interceptor 是處在 stack 中的哪一個位置以及我們前後的 interceptor 又是哪些,我們一樣可以很簡單的去完成 interceptor 需要執行的功能!</p> <p style="TEXT-ALIGN: left">這種 Layered 結構讓我們的 web application 保持簡單乾淨,甚至提昇了 interceptor 的 readabililty, testing 與 flexibility 特性。</p> <p style="TEXT-ALIGN: left">對於 readability 來說,因為每一個 interceptor 可以是獨立的,也可以與其他 interceptors 合作,但是對於我們的程式來說並不用考慮這樣的問題,我們只需要專心的撰寫我們 interceptor 所要提供的功能就好,剩下的是就交給 Struts2 framework 來幫我們完成!這樣就可以增加整個 interceptor 程式碼的可讀性,而且與 framework 之間的 coupling 降低,而 cohesion 提昇。</p> <p style="TEXT-ALIGN: left">同樣的,testing 也會變得相對的容易的多,因為我們不必知道太多其他 detail 而可以很容易的去測試我們的程式碼。</p> <p style="TEXT-ALIGN: left">最後就是 flexibility,就如同之前所說的,因為我們的 interceptor 不必知道其他 interceptors 之間的資訊,但是彼此間又可以合作,所以我們就可以讓我們的 interceptor 既可以獨立運作又可以互相合作,並且我們的 interceptor 可以根據 configuration file 的設定來改變 interceptor 之間的位置來達到不同的效果,這樣就是增加了 flexibility。</p> <p style="TEXT-ALIGN: left">對於初次接觸 Struts2 framework 的使用者來說,對於 interceptor 的設定並不是哪麼的容易,因為有些 interceptors 是 Struts2 原先就提供給 programmer 很方便的工具,如果我們忘記將這些 interceptors 加入 stack 中,那我們的 web application 就會跑不起來。雖然我們之前有說過我們可已將 interceptors 之間的位製作調整,但是有些 interceptors 要先被放置在 stack 的上面(第一次先被執行),之後的 interceptors 才能夠發揮其作用,所以彼此之間是有那麼一點關聯性存在。幸好 Struts2 framework 提供了 defaultStack 讓初學的人對於設定 interceptor stack 不用在煩心,而且對於初學者來說,defaultStack 中所安排的 interceptors 幾乎都可以滿足需求!那你可能會問,我們在<a href="http://silver8250.blogspot.com/2009/01/struts2-hellostruts2.html">初啼試聲 HelloStruts2</a> 中好像沒有設定我們的 defaultStack 耶!的確,我們並沒有很明確的設定我們的 defaultStack,不過當我們對 <package> tag 設定 extends attribute 時就已經間接的設定我們的 defaultStack!因為 defaultStack 是被設定在 struts-default 的 package 中,當我們 package extends struts-default 就繼承了 struts-default 中的所有設定!所以對於初學的 programmer 來說,我們就使用 defaultStack 作為我們的 interceptor stack!</p>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com2tag:blogger.com,1999:blog-6990680403271933461.post-7166073973513692992009-03-07T14:47:00.001+08:002009-03-07T14:47:05.089+08:00[Agile Method] Pair-Programming 與成本<p>那天去關貿網路面試時,技術長有問道:企業老闆怎麼可能採用 pair-programming,怎麼可能讓兩個同時只做一件事情?有沒有數據研究可以證明兩個人工作至少等於兩個人甚至大於兩個人的成效?</p> <p>這個問題其實見怪不怪,當我還是個準碩士時,我的指導教授開了一場敏捷方法研討會,內容就是提倡敏捷方法!在會議後半段的提問時間,許多企業也對都於 pair-programming 實行上產生質疑!沒錯~依照台灣對於成本 cost down 的觀念來說,要執行這種不符合成本的作法實在不合邏輯~雖然我在實驗室有採用過這種方法來開發軟體,不過因為我們的軟體規模實在是很小,不能說是一種 product 只是計畫中的一個 project 而已,所以實際的成效很難信服,但是兩個人的討論確實可以減輕很多壓力!</p> <p>後來我就找我的老師討論,老師應該也已經想過這問題如何在台灣實行的問題,所以他就給我這樣的回答:做軟體的品質一定要第一,成本才是其次;沒有品質的軟體賣不出去,那成本壓的在低也是不符合成本!但是如果老闆真的有成本上的壓力,我們可以部份採用 pair-programming,其實 Agile method 只是一套方法,而方法的內容可以根據實際上得情形加以調整,以便因應不同環境!所以我們可以在軟體的核心元件採用 pair-programming 而其餘的部份像是 UI 就採用傳統的 single role 方式來開發,畢竟軟體的核心元件是整個系統中最重要的,重要的部份就必須要提高其品質!</p> <p>聽完老師這樣的回答後,對於 Agile method 的可行性又更往前邁了一步!而且我也很贊同老師所說得:品質第一!</p>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-20555386402247055342009-03-07T13:37:00.001+08:002009-03-07T13:37:18.987+08:00[Interview] 關貿網路的兩次面試<p>其實已經事隔幾天了,現在才分享一下!話說,那天是 2/20,是我第一次到<a href="http://www.tradevan.com.tw/">關貿網路</a>面試,因為之前已經面試過英丰寶了,所以面試時已經不會太緊張!第一次面試是跟課長談,不過一開始 HR 就先進會議室跟我聊聊,拿了一張公司的簡介單給我,並且很仔細的跟我介紹公司的狀況、對於研發替代役的資訊、過去國防役學長的經驗分享以及公司的福利等等。接下來就是課長近來跟我面試了!正式的面試就開始~</p> <p>一開始因為課長才剛拿到我的履歷,所以就先簡短的自我簡介一下~接下來就聊聊關於學校的學習狀況、論文的內容啦~其餘的時間都是 focus 在我的實務經驗,因為我自己為了家計必須要打工賺錢,所以上了碩士後就是以技術為導向的打工經驗,接一些學校的 cases 等,這樣就聊了快一個小時,其實過程很愉快~課長不會給你很大的壓力~也希望大家像是朋友一樣的聊天模式,所以過程很輕鬆~後來我就開始提問,課長也很仔細的分析給我聽,所以面試的後半段就幾乎是課長在講給我聽~因為自己還沒有出社會,對於軟體業並不是很瞭解,只是因為自己喜歡寫程事兒想進入這行業罷了!課長就先分析了軟體業在市場上的各種領域等。最後課長從手中的資料中發現了一張考卷!接著他就說:時間不多了!我們就完成一下例行公事好了!接著就要我寫考卷中最後一個題目,題目很簡單,就是寫個 class 描述一下某個 entity 就這樣!之後就是 HR 進來跟我談薪資的部份(因為是我問課長,他說他也不知道XD 所以就請 HR 跟我說明)。第一次的面試感覺很輕鬆愉悅,課長並不會刁難你,大家在聊天中度過了進兩個小時的面試~</p> <p>關貿網路公司很大,因為第一次坐錯電梯,坐到只有到 E 棟 8 樓以上的那部XD,因為我是要到 6 樓,所以想說去 8 樓換電梯,門打開才發現,這裡也是關貿XD,他們好像是 6~8 樓都是的樣子XD</p> <p>後來因為等待面試結果很痛苦,所以我就主動打電話給 HR,HR 也說正想要通知我,所以我就準備要去第二次面試了~</p> <p>3/3 是第二次面試,對象是跟技術長以及一位也是很資深的技術部門長官,這次的面試過程只有短短的 30 分鐘!一開始也是自我簡介,接著兩位面試官就根據我的打工經驗裡問問關於遇到的困難,要怎樣解決等等的問題,問題也不會刁難,只是從中發現我的人格特質(跟去英丰寶的總經理面試一樣),因為我在實驗室學的是 Agile method,他們也有問一些問題:CMMI 如果要你寫很多文件,以你做 agile method 的人來看你有怎樣的看法?其實我的回答不是很八股,因為我不希望去捧長官,雖然說講話要甜一點,可是我就很真實的去回答我想要說得,不過還是態度有比較微婉~技術長其實也不會不高興,因為我們整個面試中也算是有說有笑,面試也不會有太大的壓力~就這樣,結束了短短 30 分鐘的面試過程~</p> <p>現在的我又進入等面試結果的狀態~等待是很痛苦的~</p>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-39365984550291730002009-03-05T10:40:00.000+08:002009-03-05T10:40:00.340+08:00[Agile Method] Communication: The key to Agile Method<p>在 Agile method 中有很多現有的方法被採用,像是 Test-Driven Development(TDD)、Pair programming 等,也有一些新的規範被納入,如:stand-up meeting、on-site customer 等!不過這些方法最重要的基礎就在於 Communication(溝通)!Agile method 中很講究溝通,就像 pair programming 就是最明顯也是強需要溝通的一項方法,溝通必須是基於自己肯分享肯討論的個性為主,不然 pair programming 也會流於形式!</p> <p>Pair programming 在之前也有討論過,就是兩個人綿密的溝通,一個人的思考畢竟有限,想法也會有缺陷,所以透過 pair programming 的方式可以降低這樣的缺陷,不過這只有降低,並不能完全的消除,畢竟還是會有機率發生在兩個人都剛好沒有想到某個缺陷的存在!因為我在實行 pair programming 時真的是有發生這樣的情況~在 pair programming 中溝通就變得很重要,溝通是基於表達方式,如果表達方式不佳,溝通就容易造成失敗,因為對方聽不懂你在說什麼。</p> <p>TDD 方法中也是有溝通,很難想像吧!當初我也是沒有想到,不過在某一堂課程中,我們老師點出了這個觀念我就懂了!TDD 就是先將我們的 test code 根據 scenario 完成,當我們完成這部份的功能時,我們就交給電腦去幫我們驗證邏輯,這種就是跟電腦的溝通,因為當 scenario 很豐富時,我們可能要測試的 test case 就會很多,這當然不能由人自己慢慢的測試,我們只能測其中幾個比較重要的,剩下的完整測試就交由電腦幫我們去跑,所以這也是一種溝通,只是不是面對人,是面對機器!</p> <p>Stand-up meeting 就像是現在有很多公司會每週開會一次,不過在 Agile method 中是希望可以每天早上開會,開會中決定今天的工作,開會中討論昨天可能碰到的問題或是開發中可能遇到的狀況等等,也因為一個開發團隊的人員很少(6~10人),所以 stand-up meeting 是相對的容易,當團隊擴大時,溝通也會是一個很大的問題!目前有些國外的 conference 也在徵求類似的經驗分享 papers,希望業界中有人可以分享他是如何解決團隊擴大時溝通的問題!</p> <p>On-site customer 就是希望我們的客戶能夠跟著開發團隊一起工作,因為只有 on-site customer 可以真正的瞭解 customers 的需求,如果開發團隊在開發中遇到問題,就可以馬上請教 on-site customer 回答問題~這也是一種溝通,不過這種溝通是希望可以讓 customer 提早進入軟體開發的生命週期,以往的開發方法中,customer 只會參與到整個生命週期的最早與最晚,也就是 requirement 與驗收,若開發團隊無法與 customer 時時的溝通,產品往往在完成後變成一堆價值很高的垃圾,畢竟 customer 無法在一次會議或一段時間中想到所有想要的 requirement,所以如何讓需求變成 live requirements 是現行開發方法中最重要的 issue。</p> <p>從這些 Agile method 中很重要的實行方法中,我們可以發現溝通是最重要的元素,不過也會有很多人誤會 Agile method 就是靠溝通而不需要撰寫文件,溝通固然是必須的,但是沒有文件紀錄溝通中重要的資訊,那溝通也就枉然了~人類的腦袋無法記得所有溝通的內容,所以還是需要文件的輔助,不過 Agile method 中又希望文件是有必要才產生,因為當文件的數量一龐大,系統有修正時,修改文件就會變成很粗重的工作,若文件不跟著同步,那文件也是流於形式而成為昂貴的垃圾了~所以如何在文件的數量上取捨,其實也是一種學問跟經驗。</p> <p>以上是目前當了自己老師的 Agile method 課程助教時有感而發~與大家分享!</p>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com0tag:blogger.com,1999:blog-6990680403271933461.post-52496691110992477602009-02-16T20:33:00.001+08:002009-02-17T00:58:44.825+08:00[Struts2] 將物件作為 Action property<p>在之前我們所示範的範例中,Action 的 property 都是以 primary data type 為主!如果我們有一些 domain object 已經定義好了,那我們可不可以 reuse 這些 objects 呢?以 programmer 的角度來說,應該是可行!沒錯~ Struts2 的確有提供這樣的機制,讓我們可以 reuse 已經定義好的物件!例如:我們已經定義好一個 User 物件如下:</p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">class</span> User<br />{<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> name;<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> password;<br /><br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> getName()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> name;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">void</span> setName(<span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> name)<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.name = name;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> getPassword()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> password;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">void</span> setPassword(<span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> password)<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.password = password;<br /> }<br />}<br /></pre><p>User class 中包含了 username 與 password 的 properties,當然,我們的物件也一定是一個 JavaBean,這樣我們才可以針對物件的 properties 進行存取!</p><p>接下來,我們就要更改我們的 HelloUser Action 的程式,當然是要使用我們已經定義好的 User 物件作為 property。在 Struts2 中提供了兩種方式:Object-backed JavaBean properties 與 ModelDriven actions。接下來我們會分別介紹這兩種方式來達到我們的目的。</p><p><strong>Object-backed JavaBean properties</strong></p><p>Object-backed JavaBean properties 的方式是最直接的,就是直接將我們所定義好的 User 物件當作 HelloUser Action 中的 property,所以我們的 HelloUser 程式修改為:</p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><span style="font-weight: bold; color: rgb(127, 0, 85);">package</span><span style="color: rgb(127, 0, 85);"> silver8250</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">hello</span><span style="color: rgb(127, 0, 85);">;</span><br /><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">import</span><span style="color: rgb(127, 0, 85);"> com</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">opensymphony</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">xwork2</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">ActionSupport</span><span style="color: rgb(127, 0, 85);">;</span><br /><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">class</span> HelloUser <span style="font-weight: bold; color: rgb(127, 0, 85);">extends</span> ActionSupport<br />{<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> User user;<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> message;<br /><br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> User getUser()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> user;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">void</span> setUser(User user)<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.user = user;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> getMessage()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> message;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">void</span> setMessage(<span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> message)<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.message = message;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> execute()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.message = <span style="color: rgb(42, 0, 255);">"Hi~"</span>+<span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.user.getName();<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">super</span>.SUCCESS;<br /> }<br />}<br /></pre><p>上面的程式碼中只是很簡單的將我們 User 物件當作 property,而且我們不必自己去初始化這個物件,因為 Struts2 framework 會幫我們去處理這個細節。這樣的方式讓我們的 Action 看起來更簡潔有力了~</p><p>不過對應的 Jsp 頁面則是要作一些修正,因為我們原先將使用者輸入的資料是直接屬於 Action 之下,但是因為我們現在是將使用者輸入資料的對應 property 放至於 User 物件中,所以我們要將 tag 更改為:<br /></p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><span style="color: rgb(127, 0, 85);"><</span><span style="font-weight: bold; color: rgb(127, 0, 85);">s</span><span style="color: rgb(127, 0, 85);">:textfield</span> name=<span style="color: rgb(42, 0, 255);">"user.name"</span> label=<span style="color: rgb(42, 0, 255);">"Your Name"</span> value=<span style="color: rgb(42, 0, 255);">""</span> <span style="color: rgb(127, 0, 85);">/></span><br /></pre><p>很直覺吧~當然,取值的時候也是這樣~其實就是 OGNL 的更改罷了~不過現在我們並不會深入 OGNL 的功能上!</p><p><strong>ModelDriven actions</strong></p><p><a href="http://struts.apache.org/2.1.6/struts2-core/apidocs/com/opensymphony/xwork2/ModelDriven.html">ModelDriven</a> actions 是另一種可以實現與 Object-backed JavaBean properties 的方法,屬於 Struts2 framework 內建的機制,也就是將我們的 Action implements ModelDriven,並且 ModelDriven interface 有提供 Generic 的機制,所以我們的程式可以修改如下:<br /></p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><span style="font-weight: bold; color: rgb(127, 0, 85);">package</span><span style="color: rgb(127, 0, 85);"> silver8250</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">hello</span><span style="color: rgb(127, 0, 85);">;</span><br /><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">import</span><span style="color: rgb(127, 0, 85);"> com</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">opensymphony</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">xwork2</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">ActionSupport</span><span style="color: rgb(127, 0, 85);">;</span><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">import</span><span style="color: rgb(127, 0, 85);"> com</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">opensymphony</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">xwork2</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">ModelDriven</span><span style="color: rgb(127, 0, 85);">;</span><br /><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">class</span> HelloUser <span style="font-weight: bold; color: rgb(127, 0, 85);">extends</span> ActionSupport <span style="font-weight: bold; color: rgb(127, 0, 85);">implements</span> ModelDriven<User><br />{<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> User user = <span style="font-weight: bold; color: rgb(127, 0, 85);">new</span> User();<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> message;<br /><br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> User getModel()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> user;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> getMessage()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> message;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">void</span> setMessage(<span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> message)<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.message = message;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> execute()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.message = <span style="color: rgb(42, 0, 255);">"Hi~"</span>+<span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.user.getName();<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">super</span>.SUCCESS;<br /> }<br />}<br /></pre><p>這種方式並不難,只是將我們原先針對 User 物件的 getter/setter 拿掉,並且實做出 getModel() method,而且回傳我們的 User 物件就可以了!不過這裡有一點要注意,我們的 User 物件一定要初始化,否則會在執行 execute() method 時出現錯誤!</p><p>使用 ModelDriven actions 的方式,User 物件裡面的所有 properties 就會像是被嵌入到我們的 Action 一樣,也就是說,當我們在 JSP 頁面取值的時候,OGNL 就不用像 Object-backed JavaBean properties 需要先以我們的 User 物件為開始點,所以我們可以直接取 name properties:</p><p>不過採用 ModelDriven actions 時還有一點要注意,如果我們所 reuse 的物件中與 Action 中有一樣的 property,那 Action 中的 property 就會被隱藏~例如:我們將 HelloUser Action 更改為:<br /></p><pre style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"><span style="font-weight: bold; color: rgb(127, 0, 85);">package</span><span style="color: rgb(127, 0, 85);"> silver8250</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">hello</span><span style="color: rgb(127, 0, 85);">;</span><br /><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">import</span><span style="color: rgb(127, 0, 85);"> com</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">opensymphony</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">xwork2</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">ActionSupport</span><span style="color: rgb(127, 0, 85);">;</span><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">import</span><span style="color: rgb(127, 0, 85);"> com</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">opensymphony</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">xwork2</span><span style="color: rgb(127, 0, 85);">.</span><span style="color: rgb(127, 0, 85);">ModelDriven</span><span style="color: rgb(127, 0, 85);">;</span><br /><br /><span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">class</span> HelloUser <span style="font-weight: bold; color: rgb(127, 0, 85);">extends</span> ActionSupport <span style="font-weight: bold; color: rgb(127, 0, 85);">implements</span> ModelDriven<User><br />{<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> User user = <span style="font-weight: bold; color: rgb(127, 0, 85);">new</span> User();<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">private</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> name;<br /><br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> User getModel()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> user;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> getName()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> name;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">void</span> setName(<span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> name)<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.name = name;<br /> }<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">public</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">String</span> execute()<br /> {<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.name = <span style="color: rgb(42, 0, 255);">"Hi~"</span>+<span style="font-weight: bold; color: rgb(127, 0, 85);">this</span>.user.getName();<br /> <span style="font-weight: bold; color: rgb(127, 0, 85);">return</span> <span style="font-weight: bold; color: rgb(127, 0, 85);">super</span>.SUCCESS;<br /> }<br />}<br /></pre><p>使用者所輸入的 name 在送出後,我們取 name 的值就會只有取到 User 中的 name,而且使用者所輸入的 name 資料也只會被存在 User 的 name 中!所以這點要小心,否則當出現問題時會很不容易發現!</p><p><strong>Conclusion</strong></p><p>雖然 Struts2 有兩種方式可以讓我們 reuse 已經撰寫好的物件,不過我個人是比較喜歡也比較建議使用 Object-backed JavaBean properties 的方式,因為這種方式比較直覺,而且我們可以在 Action 中 reuse 一個以上的物件,而 ModelDriven actions 的方式就不行!而且在撰寫頁面中的 OGNL 時也比較好 debug~畢竟他很明確的指出是在往下一層!</p><p>不過還是看個人習慣囉~</p>Silver Suhttp://www.blogger.com/profile/05871815610698130774noreply@blogger.com1