本文來自:曹勝歡部落格專欄。轉載請註明出處: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對象的方法為:
ActionContextcontext=ActionContext.getContext(); --得到Action執行的上下文Maprequest=(Map)context.get("request");--得到HttpServletRequest的Map對象Mapsession=context.getSession();--得到HttpSession的Map對象Mapapplication=context.getApplication();--得到ServletContext的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(控制反轉)方式,與之對應的三個介面和它們的方法如下所示:
public class SampleAction implementsAction,RequestAware, SessionAware, ApplicationAware{private Map request;private Map session;private Map application;@Overridepublic void setRequest(Map request){this.request = request;}@Overridepublic void setSession(Map session){this.session = session;}@Overridepublic void setApplication(Map application){this.application = application;}}
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接收資料的屬性這個地方,我們就不是在一一定義這些屬性的私人變數了,我們直接定義一個對應實體類的私人對象就可以了,代碼如下:
package com.bzu.action;publicclass LoginAction extends ActionSupport { private User user; public User getUser() { returnuser; } publicvoid setUser(User user) { this.user = user; } public String execute(){ if(user.getUsername().equals("admin")&&user.getPassword().equals("123456")) return"success"; return"fail"; }}
3.定義表單,這裡我們需要注意一下,這裡表單裡面的控制項的name屬性定義有一定的要求,定義name時我們應該定義為:對象.屬性的形式,範例程式碼:
<s:form action="LoginAction"> <s:actionerror/> <s:textfield name="user.username"></s:textfield><s:password name="user.password"></s:password> <s:submit value="提交" ></s:submit> </s:form>
4.配置struts.xml,這裡配置和平常一樣,這裡就不再重複了
至此,我們簡單的實體類封裝請求參數就完成了,我相信大家一定也會感覺很簡單吧
2.模型驅動封裝請求參數
模型驅動是指使用JavaBean來封裝來回請求的參數.這種方式的好處就是減少了action的壓力。既用於封裝來回請求的參數,也保護了控制邏輯,使它的結構清晰.這就是模型驅動的優勢.
下面我們具體來看一下模型驅動的具體實現:
模型驅動的實現主要是體現在action上
1.首先建立一個實體,比較簡單,這裡就不再寫了。
2.建立action類,繼承自ActionSupport,實現ModelDriven介面,這個介面定義了一個getModel()方法,用於返回定義的Model,然後調用set方法,進行賦值。程式碼範例:
publicclass LoginAction3 extends ActionSupport implementsModelDriven<User> { private User user=new User();//這裡記住要執行個體化 private LoginService loginService=new LoginServiceImpl();//這裡是調用登入的業務處理邏輯@Override public User getModel() { //TODOAuto-generated method stub return user;} public String execute(){System.out.println(user.getUsername());System.out.println(user.getPassword()); if(loginService.isLogin(user.getUsername(),user.getPassword())){ return SUCCESS;} return INPUT;}}
在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,一個對應表單提交的資料,另一個用與持久層。