SSH(Struts2+Spring+Hibernate)是最為 Java 業(yè)界熟知的 Java EE Web 組件層的開(kāi)發(fā)技術(shù)。很多人提起 Java EE,甚至都會(huì)將其誤認(rèn)為就是 SSH。無(wú)論是書(shū)籍還是電子教程,大部分都已經(jīng)千篇一律,講解各種標(biāo)簽、配置的用法。許多人包括筆者在內(nèi),第一次使用 SSH 的時(shí)候,按照教程的介紹進(jìn)行開(kāi)發(fā)。繁瑣的配置,重復(fù)的修改配置,不斷定義的參數(shù)轉(zhuǎn)換器,真的讓筆者苦不堪言。本文對(duì) SSH 的開(kāi)發(fā)模式嘗試了重新定義,按照規(guī)約優(yōu)于配置的原則,利用 Java 反射、注解等技術(shù),設(shè)計(jì)了新的一套 SSH 開(kāi)發(fā)框架,應(yīng)用該框架,可以大大提高開(kāi)發(fā)效率,筆者多次將該開(kāi)發(fā)框架應(yīng)用在各種小型 SSH Web 應(yīng)用系統(tǒng)之中,屢試不爽。
閱讀文章之前,讀者需要對(duì) SSH 的結(jié)合開(kāi)發(fā)有一些了解,最好是有實(shí)踐的經(jīng)驗(yàn),特別是對(duì) Struts2 需要較為了解,掌握 Struts2 自定義攔截器、自定義驗(yàn)證器等的開(kāi)發(fā)。另外,讀者還需要掌握一些前提技術(shù),包括 Java 反射、Java 注解、理解事務(wù)隔離級(jí)別等。
文章首先進(jìn)行框架的總體介紹,然后分點(diǎn)介紹各個(gè)部分的詳細(xì)設(shè)計(jì)。另外,文章還將列舉該框架中采用的技術(shù)特點(diǎn)及其相應(yīng)目的,希望讀者可以從中獲益。
文章之所以說(shuō)框架是通用的,因?yàn)樗乃枷脒m應(yīng)任何的業(yè)務(wù)需求。按照文章介紹,按照介紹的框架搭建完代碼架構(gòu)后,可以屏蔽許多技術(shù)細(xì)節(jié),讓開(kāi)發(fā)人員專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),這些繁瑣的技術(shù)細(xì)節(jié)包括技術(shù)配置、權(quán)限控制、頁(yè)面跳轉(zhuǎn)、錯(cuò)誤處理等等。框架大致的風(fēng)格如圖 1 所示,總體來(lái)說(shuō),該框架遵守了“規(guī)約優(yōu)于配置”的原則。
圖 1. 框架大致風(fēng)格( 查看大圖 )

上圖中,系統(tǒng)只有一個(gè) action 配置。每個(gè)業(yè)務(wù)操作,不再對(duì)應(yīng)一個(gè) ActionSupport 子類,而是對(duì)應(yīng)一個(gè) ActionSupport 子類的類方法,利用 Struts2 的動(dòng)態(tài)方法特性,使得業(yè)務(wù)方法擺脫了繁瑣的配置,方便的增加和刪除。全面的 result 配置,解決了各種頁(yè)面跳轉(zhuǎn)的問(wèn)題。Action 方法的起名,遵守了權(quán)限與業(yè)務(wù)模型規(guī)約,讓模型選擇與權(quán)限控制交給框架來(lái)實(shí)現(xiàn),同時(shí)訪問(wèn)的操作名就是【方法名】 .action。下面,將一一對(duì)框架的各部分進(jìn)行講解。
該框架中,我們建議只定義少量的類繼承于 ActionSupport,這樣可以使得 struts.xml 的配置盡量減少,甚至只進(jìn)行少量配置,而不用隨著業(yè)務(wù)的增加而修改配置。清單 1 是筆者為一個(gè)教師考勤系統(tǒng)定義的 Action 配置。
清單 1. Struts2 的配置清單
<action name="*" method="{1}" class="Main"> <interceptor-ref name="fileUpload"> <param name="maximumSize">4073741824</param> </interceptor-ref> <interceptor-ref name="myInterceptorStack"></interceptor-ref> <result name="input">/noDir/error.jsp</result> <result type="json" name="success"></result> <result name="errorJson" type="json"></result> <result type="json" name="error"></result> <result type="stream" name="stream"> <param name="contentType">${contentType}</param> <param name="contentDisposition">fileName="${inputFileName}"</param> <param name="inputName">inputStream</param> </result> <result name="dynamic">/${url}</result> <result name="otherAction" type="redirectAction">/${url}</result> <result name="red" type="redirect">/${url}</result> </action>
清單 1 定義了很多規(guī)則。首先,該配置使用了動(dòng)態(tài)方法調(diào)用技術(shù),這可以使得許多的 Action 方法可以聲明到一個(gè)類里,不用重復(fù)定義 Action 類,同時(shí)對(duì)業(yè)務(wù)的增加和刪除可以簡(jiǎn)約到對(duì) Action 類里方法增加和刪除,增加的 Action 方法不需進(jìn)行其他配置,如果業(yè)務(wù)被刪除,則只需要將方法注釋或者刪除,非常方便。第二,配置的 package 繼承于 json-default,也就是說(shuō)這個(gè)包里的 action 是支持 Ajax 調(diào)用的,默認(rèn)的,如果返回 ERROR、SUCCESS,則會(huì)將 Action 序列化為 JSON 返回到客戶端。我們定義了各種的跳轉(zhuǎn)類型,包括重定向到頁(yè)面(具體的頁(yè)面由 Action 里的 url 指定)、重定向到 Action、重定向到錯(cuò)誤頁(yè)面、流類型的返回等等,這為我們動(dòng)態(tài)的選擇返回結(jié)果數(shù)據(jù)提供了方便。那么使用上述的配置,對(duì)我們開(kāi)發(fā)有什么好處呢?假設(shè)有一個(gè)新的業(yè)務(wù),我們只需要在 Action 添加新的方法,如清單 2 所示。
清單 2. 添加新業(yè)務(wù)方法
public String business() throws Exception { …business process… if (has error) { addFieldError(“error message”) return INPUT; } else { //url是action中定義的一個(gè)String變量,指定跳轉(zhuǎn)地址 url = “business.jsp”; return “dynamic”; } }
調(diào)用這個(gè)新的業(yè)務(wù)方法,只需要調(diào)用這個(gè)鏈接:http://{host}:{port}/{webapp}/business.action。正如我們看到的,定義了新的業(yè)務(wù)邏輯方法,我們沒(méi)有修改或添加任何配置,因?yàn)槲覀兊呐渲檬峭暾模紤]到了各種跳轉(zhuǎn)、錯(cuò)誤情況,同時(shí)動(dòng)態(tài)方法調(diào)用特性,讓我們可以動(dòng)態(tài)的指定業(yè)務(wù)方法。如果我們對(duì)清單 2 中的跳轉(zhuǎn)代碼進(jìn)行抽取,清單 2 會(huì)更加簡(jiǎn)單。如清單 3 所示。
清單 3. 抽取基礎(chǔ)方法后的業(yè)務(wù)方法
public String business() throws Exception { …business process… if (has error) { return redirectToErrorPage("error message") } else { return redirectToPage("business.jsp"); } }
上面的清單中,我們抽取了 redirectToErrorPage 和 redirectToPage 方法,這樣就可以使得其他的業(yè)務(wù)方法可以重用這些跳轉(zhuǎn)方法,整個(gè)業(yè)務(wù)過(guò)程變得清晰易懂。類似的我們還可以抽取出 redirectToAction(跳轉(zhuǎn)到另一個(gè)業(yè)務(wù)方法)、redirectStream(流類型的跳轉(zhuǎn))、redirectToAnotherPage(用于重定向的跳轉(zhuǎn))、redirectToJson(Ajax 的跳轉(zhuǎn))等等。這樣這些公共方法就可以讓其他的開(kāi)發(fā)人員一起使用。程序員可以從跳轉(zhuǎn)、錯(cuò)誤提示、重復(fù)配置 Action 的痛苦中解救出來(lái),專注于編寫(xiě)業(yè)務(wù)邏輯。
有了上面的配置,我們還不能做到完全脫離配置。比如,我們定義了一個(gè) Action 類繼承于 ActionSupport,我們知道使用 ModelDriven 可以將用戶上傳的數(shù)據(jù)封裝到一個(gè)業(yè)務(wù) Bean 里,而不用直接在 Action 里聲明變量,這很重要。我相信有很多讀者遇到過(guò)這個(gè)問(wèn)題。當(dāng)業(yè)務(wù) Bean 不同時(shí),也就是需要用戶上傳的數(shù)據(jù)不同時(shí),我們就要隨之添加新的 Action,這直接導(dǎo)致修改 struts.xml 的配置,然后還要修改 applicationContext.xml 的事務(wù)配置、Bean 的配置,哪天我們不要這個(gè)業(yè)務(wù)了,又要重復(fù)的修改刪除配置,筆者開(kāi)始時(shí)為此事近乎抓狂,為什么我們不能只定義一個(gè) Action,而 ModelDriven 的模型動(dòng)態(tài)改變呢?
仔細(xì)考慮 Struts2 的機(jī)制,Struts2 將客戶端的參數(shù)裝配到 ModelDriven 的模型里,是通過(guò)“裝配攔截器”裝配的。只要我們?cè)谘b配攔截器執(zhí)行前,改變 ModelDriven 里的模型對(duì)象就行了。這就需要我們自定義一個(gè)攔截器,struts2 提供了這個(gè)機(jī)制。在自定義的攔截器里,我們根據(jù)用戶調(diào)用的 Action 方法,新建一個(gè)模型,并且將模型設(shè)置到 Action 中,這樣模型就可以是動(dòng)態(tài)的了,記住,這個(gè)攔截器需要放在 defaultStack 的前面。
同樣,新的問(wèn)題是如何根據(jù) Action 方法動(dòng)態(tài)的選擇業(yè)務(wù)模型呢?難道重復(fù)的寫(xiě) if 方法嗎?當(dāng)然不能這樣,動(dòng)態(tài)的模型,就應(yīng)該來(lái)自于動(dòng)態(tài)的方法。因此定義的 action 方法需要有規(guī)約,筆者在自己的程序中,是這樣定義規(guī)約的。Action 方法是這樣組成:_$_,使用美元符隔開(kāi)(美元符是 Java 方法合法的標(biāo)識(shí)符),前部分是操作名,可以任意取,后部分是業(yè)務(wù)模型的類名。同時(shí),所有的業(yè)務(wù)模型,都放到指定的一個(gè)包里,假設(shè)該包名為 com.dw.business。那么在自定義的攔截里,我們獲得用戶調(diào)用的 Action 方法名,按美元符隔開(kāi)獲得后半部分的類名,指定的包名(這里是 com.dw.business)+ 類名就是業(yè)務(wù)模型類的全路徑,使用 Java 反射機(jī)制動(dòng)態(tài)生成一個(gè)空的業(yè)務(wù)模型對(duì)象(所以,業(yè)務(wù)模型類必須有一個(gè)無(wú)參的構(gòu)造函數(shù)),設(shè)置到 Action 里,再交給裝配器的時(shí)候,裝配器會(huì)自動(dòng)組裝這個(gè)模型。該攔截器的核心代碼如清單 4 所示。
清單 4. 攔截器清單
public String intercept(ActionInvocation ai) throws Exception { ai.addPreResultListener(this); Main action = (Main)ai.getAction(); //業(yè)務(wù)方法名 String name = ai.getInvocationContext().getName(); int lastIndex = name.lastIndexOf("{1}quot;); if (lastIndex != -1) { try { String head = "com.dw.business."; String className = name.substring(lastIndex+1, name.length()); //關(guān)鍵:動(dòng)態(tài)設(shè)置業(yè)務(wù)模型 action.setModel(Class.forName(head).newInstance()); } catch (Exception e) {} } return ai.invoke(); }
有了上面的配置,基本可以做到屏蔽大多數(shù)技術(shù)細(xì)節(jié)開(kāi)發(fā)了,但是我們還有一個(gè)問(wèn)題,當(dāng)在業(yè)務(wù)方法中,意外拋出了異常,struts2 默認(rèn)的是返回 INPUT,按照 清單 1 的配置,發(fā)生錯(cuò)誤將跳轉(zhuǎn)到 noDir/error.jsp 頁(yè)面,顯示錯(cuò)誤信息,這適合在非 Ajax 的處理。但是有時(shí)候我們希望他返回錯(cuò)誤是 JSON 類型,因?yàn)?Action 的一些方法是 Ajax 的調(diào)用方式,也就是 Action 方法的執(zhí)行結(jié)果需要返回 errorJson。我的解決方法是利用 Java 注解技術(shù),定義一個(gè)新的注解,名為 IfErrorReturnToJson,它的代碼如清單 5 所示。
清單 5. IfErrorReturnToJson 的代碼
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface IfErrorReturnToJson { }
該注解作用在方法上,也就是 Action 的業(yè)務(wù)方法,如果我們希望某業(yè)務(wù)方法在發(fā)送錯(cuò)誤時(shí),就返回 JSON 類型,那么我們只需要在該方法上加上這個(gè)注解。現(xiàn)在,我們?nèi)绾蝿?dòng)態(tài)的修改返回值呢。 清單 2 的第二行,為 ActionInvocation 添加了 PreResultListener,這個(gè)監(jiān)聽(tīng)器是在返回結(jié)果前進(jìn)行一些處理,正符合我們的需求。我們?yōu)樽远x攔截器類實(shí)現(xiàn)了 PreResultListener 接口,實(shí)現(xiàn)了接口方法,如清單 6 所示。
清單 6. PreResultListener 的代碼
public void beforeResult(ActionInvocation ai, String result) { if (Main.INPUT.equals(result) || Main.ERROR.equals(result)) { try { String methodName = ai.getInvocationContext().getName(); Method method = Main.class.getMethod(methodName); if (method != null && method.getAnnotation( IfErrorReturnToJson.class) != null) { ai.setResultCode(Main.ERROR_JSON); } } catch (Exception e) { } } }
可以看到,我們?cè)诜祷亟Y(jié)果前,如果當(dāng)前的返回結(jié)果是 INPUT 或者 ERROR,我們會(huì)使用反射機(jī)制,檢查該方法是否加了 IfErrorReturnToJson 的注解,如果加了注解,則調(diào)用 ai.setResultCode(Main.ERROR_JSON); 方法,修改返回值,使得結(jié)果為 JSON 數(shù)據(jù)類型。
在 SSH 開(kāi)發(fā)中,需要重點(diǎn)考慮的是 Spring 的事務(wù)配置。根據(jù)不同的業(yè)務(wù),定義的事務(wù)隔離級(jí)別就不同,比如對(duì)隔離級(jí)別要求高的,就要用到 Spring 的序列化讀的隔離配置;有一些方法只是為了權(quán)限控制,用于頁(yè)面跳轉(zhuǎn),則不需要用到事務(wù)控制;一些操作目的是搜索,那么該事務(wù)就是只讀的。精細(xì)的事務(wù)配置,可以提高業(yè)務(wù)處理的代碼效率。這時(shí)候,業(yè)務(wù)方法的命名規(guī)約就可以起到隔離級(jí)別的控制作用,比如方法名是 *begin*( 方法名中包含 begin),則該方法沒(méi)有事務(wù)控制;方法名是 *search* 的,則該方法擁有只讀的事務(wù);方法名是 *seri*,則用序列化讀事務(wù)控制,提高并發(fā)安全級(jí)別。這個(gè)事務(wù)配置清單如所示。
在一些應(yīng)用中,涉及到簡(jiǎn)單的權(quán)限管理。讀者可能會(huì)想到一些開(kāi)源的中間件,如 ralasafe 這樣的開(kāi)源權(quán)限組件,雖然很全面,但是學(xué)習(xí)起來(lái)需要一定的時(shí)間,熟練掌握,并配合 SSH 開(kāi)發(fā)則更需要細(xì)致的學(xué)習(xí)。當(dāng)我們只是簡(jiǎn)單的權(quán)限控制時(shí),我們完全可以用到一些規(guī)約,來(lái)完成用戶的權(quán)限控制。在我設(shè)計(jì)的規(guī)約中,需要在數(shù)據(jù)庫(kù)中定義一張 Permission 表,代表權(quán)限 , 它與用戶表是多對(duì)一的關(guān)系,它至少有兩個(gè)字段,一個(gè)是 permissionName,一個(gè)是 permissionPrefix。permissionName 用于描述該權(quán)限,而 permissionPrefix 是我們所關(guān)心的。我們?cè)O(shè)計(jì) Action 的方法,遵守 permissionPrefix_actionMethodName$BussinessBean 這樣的原則,permissionPrefix 前綴代表數(shù)據(jù)庫(kù)中權(quán)限表的 permissionPrefix 值 , 使用下劃線隔開(kāi)。當(dāng)用戶登錄后,會(huì)將該用戶所擁有的權(quán)限列表存放到 session 之中。當(dāng)用戶試圖訪問(wèn)一個(gè) Action Method 時(shí),會(huì)使用 ModelDriven 的規(guī)約 里一樣的方法,截取方法名獲得該方法允許的訪問(wèn)權(quán)限,如果用戶的權(quán)限列表中包含了該方法的權(quán)限,則允許調(diào)用,否則不允許調(diào)用。這樣設(shè)計(jì),雖然無(wú)法做到數(shù)據(jù)的訪問(wèn)權(quán)限,卻可以滿足一大部門的功能權(quán)限控制。這里需要記住,權(quán)限描述可以修改,但是權(quán)限的 permissionPrefix 不能修改,如果方法名中沒(méi)有權(quán)限標(biāo)識(shí),則代表任何用戶都可以訪問(wèn)。類似的,如果一個(gè)方法多個(gè)權(quán)限都可以訪問(wèn),則可以這樣設(shè)計(jì)方法 p1_p2_p3_methodName$BussinessBean,使用多個(gè)下劃線分割。
基于 HTTP 協(xié)議的特殊性,上傳到 Web 服務(wù)器的數(shù)值都是字符串,而我們的業(yè)務(wù)模型都是對(duì)象類型,有一些還是復(fù)雜的業(yè)務(wù)對(duì)象。Struts2 提供了 Conveter 的機(jī)制,允許程序員將 String 轉(zhuǎn)換成復(fù)雜的業(yè)務(wù)對(duì)象。但是筆者剛用這個(gè)機(jī)制的時(shí)候,開(kāi)始的確興奮,但是隨著業(yè)務(wù)的修改,業(yè)務(wù)模型的修改,這些 Conveter 的管理著實(shí)讓人頭疼。因此,本人認(rèn)為 Conveter 技術(shù)應(yīng)該盡量少用。我提出了一個(gè)新的方法,假設(shè)一個(gè)業(yè)務(wù)模型中,里面包含一個(gè) List<People> pids 屬性對(duì)象,People 里有一個(gè) id 屬性,我們需要客戶端上傳 People 的 id 列表,放入 List<People> pids 之中。如果使用 Conveter,我們?cè)诳蛻舳诵枰x規(guī)則,如客戶端上傳 pids=1,2,3,4,6,8。然后服務(wù)器端使用 Conveter 執(zhí)行分割字符串、新建 People 對(duì)象、設(shè)置 ID、添加到列表等一系列操作。聽(tīng)著都頭暈不是嗎 ? 而且這相對(duì)并不安全,對(duì)程序員解析字符串的功底要求很高。我們感謝 Struts2 的傳參機(jī)制,如果我們這樣傳參:<input name=”pids[0].id”/><input name=”pids[1].id”/>。這兩個(gè) input 的上傳,Struts2 會(huì)根據(jù)上傳數(shù)據(jù)的 name,自動(dòng)組裝成 List<People> 對(duì)象,并且賦值給 pids,pids[0] 代表列表的第 0 個(gè)元素,這用到了 OGNL 表達(dá)式的傳參規(guī)則,它會(huì)自動(dòng)識(shí)別 pids 是數(shù)組還是列表,如果 pids 默認(rèn)是空,它還會(huì)自動(dòng)新建一個(gè)空的數(shù)組或列表。筆者可以查看 OGNL 的相關(guān)教程。合理利用該技術(shù),可以大大縮減傳參的難度。
Struts2 提供了眾多的標(biāo)簽,這些標(biāo)簽大致包括了 UI 標(biāo)簽、數(shù)據(jù)標(biāo)簽以及邏輯標(biāo)簽。這里,我們需要理解 UI 標(biāo)簽與服務(wù)器端的數(shù)據(jù)交互格式,比如 checkboxlist 標(biāo)簽,傳到服務(wù)器端就是一個(gè)數(shù)組,該數(shù)組存放的是選定的 checkbox 列表的數(shù)值 , 這比使用 iterator 標(biāo)簽生成 checkbox 列表容易獲得數(shù)據(jù)。Doubleselect 標(biāo)簽,用于生成 2 級(jí)的級(jí)聯(lián) select,經(jīng)常使用的是部門 - 用戶的級(jí)聯(lián)。Optiontransferselect 標(biāo)簽,用于批量的遷移,比如用于批量的為部門分配用戶。使用 struts2 的標(biāo)簽可以大大減少用戶界面的開(kāi)發(fā),以及使得用戶界面到服務(wù)器的數(shù)據(jù)傳輸方式變得非常簡(jiǎn)單。
Struts2 具有一套完善的驗(yàn)證機(jī)制,在 ActionSupport 類里,可以將模型驗(yàn)證方法寫(xiě)在 validate* 方法里。如果重寫(xiě)了 ActionSupport 的 validate 方法,這個(gè) validate 會(huì)在執(zhí)行所有 Action Method 前調(diào)用,執(zhí)行驗(yàn)證。而自定義的 validate*(* 是方法名),比如方法名是 add,則這個(gè)方法名就是 validateAdd),它只會(huì)在執(zhí)行 add 方法前執(zhí)行驗(yàn)證。對(duì)于數(shù)據(jù)的驗(yàn)證,筆者建議使用 validater 文件驗(yàn)證,它更加容易配置和修改,利用現(xiàn)有的驗(yàn)證器,可以減少硬編碼驗(yàn)證的痛苦。在上面提到的框架中,由于使用的是動(dòng)態(tài)方法機(jī)制,我們需要在 Action 類所在的包里,新建 validator 的 XML 文件,名字是 { 類名 }-{ 方法名 }-validation.xml(如果不是動(dòng)態(tài)的方法,則 { 類名 }-validation.xml 就足夠了)。最后的結(jié)果如圖 2 所示。
圖 2. Validator 文件配置結(jié)果

具體的配置內(nèi)容,讀者可以搜索 Struts2 的 validation 框架教程。
Struts2 提供的驗(yàn)證器,包括 date、required、requiredstring 等等,這些可以歸于數(shù)據(jù)驗(yàn)證。而對(duì)于特定的業(yè)務(wù)模型驗(yàn)證,則比較復(fù)雜,因此,在該框架中,可以自定義一個(gè)業(yè)務(wù)驗(yàn)證器,這是 struts2 支持的,它負(fù)責(zé)對(duì)業(yè)務(wù)模型進(jìn)行驗(yàn)證,比如可以驗(yàn)證用戶上傳的用戶 ID 的用戶是否存在,這可以在很大程度上保證系統(tǒng)的安全性。驗(yàn)證器的代碼清單 7 如下面所示。
清單 7. 業(yè)務(wù)驗(yàn)證器代碼
public class BusinessValidator extends FieldValidatorSupport { private String property = null; public String getProperty() { return property; } public void setProperty(String property) { this.property = property; } @Override public void validate(Object exist) throws ValidationException { String fieldName = getFieldName(); Object fieldValue = getFieldValue(fieldName, exist); if (fieldValue != null && fieldValue instanceof Integer && ((Integer)fieldValue) <= 0) { addFieldError("message", "上傳的ID,該數(shù)據(jù)是不存在!"); } else if (fieldValue == null) { addFieldError("message", "上傳的ID,該數(shù)據(jù)是不存在!"); } else { if (exist != null && exist instanceof Main && ((Main)exist).getModel() instanceof IChecker) { Main pa = (Main)exist; IChecker e = (IChecker)pa.getModel(); boolean isRight = e.checkOk(property, pa); if (!isRight) { addFieldError("message", "上傳的ID,該數(shù)據(jù)是不存在!"); } else { pa.getPubDao().getHibernateTemplate().clear(); } } } } }
清單 7 中,用到一個(gè) IChecker 接口,需要驗(yàn)證的業(yè)務(wù)模型需要實(shí)現(xiàn) IChecker 接口,在接口實(shí)現(xiàn)方法中實(shí)現(xiàn)業(yè)務(wù)驗(yàn)證過(guò)程,錯(cuò)誤的話返回 false。
在 src 目錄(或者 WEB-INF 下的 class 目錄),添加 validater.xml 文件,在其他自帶的驗(yàn)證器后,添加業(yè)務(wù)驗(yàn)證器配置,命名為 check。
清單 8. 驗(yàn)證器的配置
<validators> …… <validator name="check" class="com.attendance.action.BusinessValidator"/> </validators>
接下來(lái)就可以使用這個(gè)驗(yàn)證器,在圖 2 中的 validation 文件里,添加如清單 9 的配置。
清單 9. 驗(yàn)證器的使用
<field-validator type="check" short-circuit="true"> <param name="property">department</param> <message> 該部門不存在 !</message> </field-validator>
利用 JEE Eclipse 生成 Hibernate 的 JPA 模型
IBM 提供了 JEE 版的 Eclipse,專門用于開(kāi)發(fā) Java EE 的應(yīng)用,它提供了從數(shù)據(jù)庫(kù)中生成符合 JPA 規(guī)范的數(shù)據(jù)模型的能力。Hibernate3 以后,支持了 JPA 規(guī)范(不是完全支持,但是已經(jīng)較為全面),利用 JEE Eclipse 的生成能力,就可以避免繁瑣的 hbm 文件的配置。同時(shí),JPA 的規(guī)范里,還可以提供 NamedQuery、OrderBy 等注解,對(duì)于復(fù)雜的數(shù)據(jù)庫(kù),該規(guī)范可以減少開(kāi)發(fā)時(shí)間。
Struts2 的 action 標(biāo)簽,用于調(diào)用某個(gè) action。這個(gè)標(biāo)簽筆者認(rèn)為非常有用,尤其體現(xiàn)在 UI 重用中,比如用戶管理中,在很多個(gè)界面里,都允許用戶信息修改和用戶刪除,那么我們就可以將用戶信息修改和用戶刪除的頁(yè)面代碼以及 JavaScript 放在一個(gè) JSP 里,同時(shí)定義一個(gè) Action 方法 ( 名為 A),為這個(gè) JSP 執(zhí)行初始化。接下來(lái)在允許進(jìn)行用戶修改和刪除的界面,都用 s:action 標(biāo)簽調(diào)用這個(gè) A 方法,設(shè)置 executeResult 為 true,將用戶修改和刪除的代碼包含在頁(yè)面里。這樣就實(shí)現(xiàn)了 Action 的重用。如果重用的界面不需要服務(wù)器的初始化,這直接使用 jsp:include 或 s:include 引用重用的 JSP。
本文通過(guò)一系列的講解,講述了如何搭建一個(gè)通用的 SSH 開(kāi)發(fā)框架,陳述了其中的設(shè)計(jì)思想,由于篇幅限制,文章只介紹了框架中的重要部分和重要思想,希望讀者可以從中獲益。由于筆者水平有限,如有錯(cuò)誤,請(qǐng)聯(lián)系我批評(píng)指正。
文章轉(zhuǎn)自: http://www.ibm.com/developerworks/cn/java/j-lo-ssh/
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
