天天看點

Java程式員從笨鳥到菜鳥之(四十三)細談struts2(六)擷取servletAPI和封裝表單資料

本文來自:曹勝歡部落格專欄。轉載請注明出處:http://blog.csdn.net/csh624366188 

一:擷取servletAPI的三種方法

       在傳統的Web開發中,經常會用到Servlet API中的HttpServletRequest、HttpSession和ServletContext。Struts 2架構讓我們可以直接通路和設定action及模型對象的資料,這降低了對HttpServletRequest對象的使用需求,同時降低了對servletAPI的依賴性,進而降低了與servletAPI的耦合度。但在某些應用中,我們可 能會需要在action中去通路HttpServletRequest等對象,是以有時候我們不得不拉近struts2和servletAPI的關系,但struts2也有盡量減少耦合度的方法,下面我們就一起具體看一下在struts2中獲得ServletAPI的三種方法:

1.ServletAPI解藕方式(一)擷取Map對象:

    為了避免與Servlet API耦合在一起,友善Action類做單元測試,Struts 2對HttpServletRequest、HttpSession和ServletContext進行了封裝,構造了三個Map對象來替代這三種對象,在Action中,直接使用HttpServletRequest、HttpSession和ServletContext對應的Map對象來儲存和讀取 資料。可以通過com.opensymphony.xwork2.ActionContext類來得到這三個對象。ActionContext是Action執行的上下文,儲存了很多對象如parameters、request、session、application和locale等。通過ActionContext類擷取Map對象的方法為:

          ActionContext中儲存的資料能夠從請求對象中得到,其中的奧妙就在于Struts 2中的org.apache.struts2.dispatcher.StrutsRequestWrapper類,這個類是 HttpServletRequest的包裝類,它重寫了getAttribute()方法(在頁面中擷取request對象的屬性就要調用這個方法), 在這個方法中,它首先在請求對象中查找屬性,如果沒有找到(如果你在ActionContext中儲存資料,當然就找不到了),則到 ActionContext中去查找。這就是為什麼在ActionContext中儲存的資料能夠從請求對象中得到的原因。

2.IOC(控制反轉)擷取servletAPI

     Action類還有另一種獲得ServletAPI的解耦方式,這就是我們可以讓他實作某些特定的接口,讓Struts2架構在運作時向Action執行個體注入request、session和application對象。這種方式也就是IOC(控制反轉)方式,與之對應的三個接口和它們的方法如下所示:

ServletRequestAware接口和ServletContextAware接口不屬于同一個包,前者在org.apache.struts2.interceptor包中,後者在org.apache.struts2.util包中,這很讓人迷惑。

3.與Servlet API耦合的通路方式

    直接通路Servlet API将使你的Action與Servlet環境耦合在一起,我們知道對于HttpServletRequest、 HttpServletResponse和ServletContext這些對象,它們都是由Servlet容器來構造的,與這些對象綁定在一起,測試時就需要有Servlet容器,不便于Action的單元測試。但有時候,我們又确實需要直接通路這些對象,那麼當然是以完成任務需求為主。要直接擷取HttpServletRequest和ServletContext對象,可以使用org.apache.struts2. ServletActionContext類,該類是ActionContext的子類,在這個類中定義下面兩個靜态方法:

1.得到HttpServletRequest對象:

public static HttpServletRequestgetRequest()

2.得到ServletContext對象:

public static ServletContextgetServletContext()

此外,ServletActionContext類還給出了擷取HttpServletResponse對象的方法,如下:

public static HttpServletResponsegetResponse()

ServletActionContext類并沒有給出直接得到HttpSession對象的方法,HttpSession對象可以通過HttpServletRequest對象來得到。

除了上述的方法調用得到HttpServletRequest和ServletContext對象外,還可以調用ActionContext對象的 get()方法,傳遞ServletActionContext.HTTP_REQUEST和 ServletActionContext.SERVLET_CONTEXT鍵值來得到HttpServletRequest和 ServletContext對象同樣的,也可以向ActionContext的get()方法傳遞ServletActionContext.HTTP_ RESPONSE鍵值來得到HttpServletResponse對象

       總結:通過上面三種方式的講解我們可以看出,三種獲得servletAPI的方式基本都差不多,通常我們建議大家采用第一種方式來擷取HttpServletRequest和ServletContext對象,這樣簡單而又清晰,并且降低了和servletAPI的耦合度,這樣也友善進行單元測試

二:struts2封裝請求參數三種方式

     在struts2開發應用中,我們可能經常要求獲得視圖層傳過來的很多資料,一般都是一個實體類的n多屬性,很多時候實體類的屬性特别多,這時候如果還是按以前的方式在action裡面一個個的定義出這些屬性的私有變量,然後在提供set、get方法的話,這樣就會使整個action太臃腫,嚴重妨礙了代碼的可閱讀性,并且也違背了代碼的可複用性,這時我們就需要對這些請求參數進行封裝,提高代碼的可複用性,下面我們就一起來具體看一下三種封裝請求參數的方法:

1.利用實體類封裝參數

        這種方式是封裝參數最簡單的方法,一般也比較常用,因為在我們的struts應用程式中,我們一般會根據資料庫的資訊寫出對應的實體類,是以這正好使我們可以利用的,下面我們看一下具體操作:

1.建立實體類user(包括使用者名和密碼屬性),這裡比較簡單,我們就不貼出代碼了。

2.建立action,這裡我們主要是來看一下action接收資料的屬性這個地方,我們就不是在一一定義這些屬性的私有變量了,我們直接定義一個對應實體類的私有對象就可以了,代碼如下:

3.定義表單,這裡我們需要注意一下,這裡表單裡面的控件的name屬性定義有一定的要求,定義name時我們應該定義為:對象.屬性的形式,示例代碼:

4.配置struts.xml,這裡配置和平常一樣,這裡就不再重複了

至此,我們簡單的實體類封裝請求參數就完成了,我相信大家一定也會感覺很簡單吧

2.模型驅動封裝請求參數

      模型驅動是指使用JavaBean來封裝來回請求的參數.這種方式的好處就是減少了action的壓力。既用于封裝來回請求的參數,也保護了控制邏輯,使它的結構清晰.這就是模型驅動的優勢.

下面我們具體來看一下模型驅動的具體實作:

模型驅動的實作主要是展現在action上

1.首先建立一個實體,比較簡單,這裡就不再寫了。

2.建立action類,繼承自ActionSupport,實作ModelDriven接口,這個接口定義了一個getModel()方法,用于傳回定義的Model,然後調用set方法,進行指派。代碼示例:

在com.opensymphony.xwork2.ModelDriven接口源代碼中有一段很重要的說明,現抄錄如下

ModelDriven Actions provide a model object to bepushed onto the ValueStack in additionto the Action itself,allowing a FormBeantype approach like Struts

翻譯:模型驅動的Action。将模型對象以及Action對象都放到ValueStack裡面,允許像Struts一樣的FormBean方式

也即:一個Action要想成為模型驅動的話,就必須實作ModelDriven接

口,而我們之前所一直繼承的ActionSupport類并沒有實作ModelDriven接口

ModelDrivenAction類的執行流程是:首先調用getModel()方法得到User對象,接着根據JavaBean的原則将用戶端傳過來的屬性,一個一個的set到User對象的屬性中,将屬性全部set完之後,再執行execute()方法。對于模型驅動,隻要了解這些就足夠了

擴充:模型驅動的底層實作機制

這裡用到了defaultStack攔截器棧中的modelDriven攔截器

它對應com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor類,其API描述如下

public class ModelDrivenInterceptor extends AbstractInterceptor

Watches for ModelDriven actions and adds the action`s model on to the valuestack.

翻譯:觀察模型驅動的Action,并将這個Action的模型【這裡指User對象】放到值棧中

Note:The ModelDrivenInterceptor must come before the bothStaticParametersInterceptor and ParametersInterceptor if you want theparameters to be applied to the model.

翻譯:若希望将表單送出過來的參數應用到模型裡面,那麼ModelDrivenInterceptor攔截器就必須位于StaticParametersInterceptor和ParametersInterceptor攔截器前面。

實際上struts-default.xml已完成這個工作了。可以在defaultStack攔截器棧中檢視三者位置,是以對于采用模型驅動的方式的話,在struts.xml中隻需要指定模型驅動的類就可以了,其它的都不需要我們手工修改

3,屬性驅動接收參數

這種方式應該不算是參數封裝的方式,但我們很多情況下都用屬性驅動的方式接收參數,因為這種方式友善,簡潔,易控制。屬性驅動在Action中提供與表單字段一一對應的屬性,然後一一set指派,采用屬性驅動的方式時,是由每個屬性來承載表單的字段值,運轉在MVC流程裡面。由于這種方式比較簡單,這裡就不在贅述了。

到底是用屬性驅動和是模型驅動呢?

1)統一整個系統中的Action使用的驅動模型,即要麼都是用屬性驅動,要麼都是用模型驅動。

2)如果你的DB中的持久層的對象與表單中的屬性都是一一對應的話,那麼就使用模型驅動吧,畢竟看起來代碼要整潔得多。

3)如果表單的屬性不是一一對應的話,那麼就應該使用屬性驅動,否則,你的系統就必須提供兩個Bean,一個對應表單送出的資料,另一個用與持久層。