WebWork是由OpenSymphony組織開發的,致力於組件化和代碼重用的拉出式MVC模式J2EE Web框架。WebWork目前最新版本是2.1,現在的WebWork2.x前身是Rickard Oberg開發的WebWork,但現在WebWork已經被拆分成了Xwork1和WebWork2兩個項目,如下示意圖所示:
Xwork簡潔、靈活功能強大,它是一個標準的Command模式實現,並且完全從web層脫離出來。Xwork提供了很多核心功能:前端攔截機(interceptor),運行時表單屬性驗證,類型轉換,強大的表達式語言(OGNL – the Object Graph Notation Language),IoC(Inversion of Control倒置控制)容器等。
WebWork2建立在Xwork之上,處理HTTP的響應和請求。WebWork2使用ServletDispatcher將HTTP請求的變成Action(業務層Action類), session(會話)application(應用程序)範圍的映射,request請求參數映射。WebWork2支持多視圖表示,視圖部分可以使用JSP, Velocity, FreeMarker, JasperReports,XML等。
下面我們提到的WebWork將爲WebWork2,使用的版本是2.1。
WebWork安裝-HelloWorld
WebWork安裝
當然,在具體開發使用介紹之前,搭建好運行環境是必備的。
首先從https://webwork.dev.java.net/servlets/ProjectDocumentList下載最新的WebWork壓縮包,並將其解壓開來。打開解壓目錄,你將看到以下的文件和目錄:
webwork-2.x.jar 當然就是WebWrok最新發布的Jar包
webwork-example.war 是WebWrok自帶的很有代表性的功能演示例子,掌握它是提高你的WebWork技術水平的捷徑
webwork-migration.jar 提供快速將1.x版本移植到2.x版本所用的類文件
docs目錄 WebWrok的使用文檔,包括api文檔、clover文檔、單元測試(Junit)文檔等
lib目錄 WebWork在運行或編譯時所用到的所有.jar包
src目錄 源程序目錄
2、WebWork是J2EE Web框架,當然要運行在Web容器中,我用的是穩定的Tomcat 4.1,關於tomcat的安裝和部署請自己搞定。
3、用WebWork當然要將它的運行時用到的Jar包放到Web容器可以找到的ClassPath中,將步驟1介紹的webwork-2.x.jar放到你部署目錄下WEB-INFlib目錄裏,同時將WebWrok解壓目錄libcore下的所有.jar文件也拷貝到WEB-INFlib目錄,這些是運行WebWork必需要用到的jar包。
4、瞭解Web框架的朋友都知道,一般Web框架都是通過一個JavaServlet控制器提供統一的請求入口,解析請求的url,再去調用相應的Action進行業務處理。WebWork也不例外,它要求你在web.xml文件裏配置一個派遣器ServletDispatcher,它初始化WebWrok的一些配置信息,解析XWork的Action配置信息,根據請求去組裝和調用執行相應的攔截器(Interceptor)、Action、Action Result(Action執行結果的輸出)等,具體配置如下:
代碼 |
…… <servlet> <servlet-name>webwork</servlet-name> <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class> </servlet> …… <servlet-mapping> <servlet-name>webwork</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> …… |
這樣,.action結尾的所有url請求將直接有ServletDispatcher去調度。下面我們寫一個經典的HelloWorld,跑一個簡單實例來驗證你運行環境是否可用,並感受一下簡單、功能強大的WebWork的開發。
注意:如果使用WebWork自帶的標籤庫,除了配置相應的標籤庫以外,還須將com.opensymphony.webwork.views.velocity.WebWorkVelocityServlet配置到web.xml,具體可以參考webwork-example裏面的配置。
首先看下面這個程序HelloWorldAction.java:
代碼 |
package helloWorld import com.opensymphony.xwork.Action; public class HelloWorldAction implements Action{ String greeting; public String getGreeting() { return greeting; } public String execute() throws Exception { greeting = "Hello World!"; return SUCCESS; } } |
HelloWorldAction是一個普通的Java類,它實現了Action這個接口。Action是一個非常簡單的接口,只有一個方法:public String execute() throws Exception; ,Action類介紹見下一節。HelloWorldAction有一個String類型字段greeting,在execute()方法中,greeting被賦值“Hello World!”,並返回String型常量SUCCESS,SUCCESS的定義詳見Action接口,這個常量代表了execute()方法執行成功,將返回成功頁面。
返回的頁面greetings.jsp代碼如下:
代碼 |
<%@ taglib prefix="ww" uri="webwork" %> <html> <head> <title>First WebWork Example</title> </head> <body> <p><ww:property value="greeting"/></p> </body> </html> |
greetings.jsp很簡單的jsp頁面,它使用了WebWork自帶的標籤庫。它的作用是輸出變量“greeting”的值。這個<ww:property value="greeting"/>語句,相當於調用相應Action(HelloWorldAction)的getGreeting()方法,取得變量“greeting”的值。
我們的HelloWorld代碼就這麼多,完了。可是,HelloWorldAction怎麼去調用、執行?執行成功它又怎麼知道返回到greetings.jsp?XWork的配置文件xwork.xml會負責將要執行的Action和展現的視圖連接起來,見xwork.xml的如下片斷:
代碼 |
<action name="hello" class=" helloWorld .HelloWorldAction"> <result name="success" type="dispatcher"> <param name="location">/greetings.jsp</param> </result> </action> |
我們先看action標籤:name=”hello”,表示我們調用這個Action的標識是hello,這樣我們可以通過下面的url訪問這個Action:…/hello.action,
例如:http://localhost:8080/webwork/hello.action;class=" helloWorld .HelloWorldAction"很好理解,這是真正調用執行的類。我們在看看result標籤:name="success",記得前面HelloWorldAction返回的字符常量SUCCESS嗎?它的值其實就是“success”,它表示Action執行成功返回success就轉向這個結果;type="dispatcher"表示執行完Action,轉向結果頁面的方式;param參數指定了結果頁面的位置:/greetings.jsp。
代碼寫完,剩下的當然是編譯、部署。啓動tomcat服務器之後我們就可以執行了:
在瀏覽器裏輸入你的地址:http://localhost:8080/webwork/hello.action
你將會看到結果
Action(動作)
Action介紹
Action在MVC模式中擔任控制部分的角色,在WebWork中使用的最多。每個請求的動作都對應於一個相應的Action,一個Action是一個獨立的工作單元和控制命令,它必需要實現XWork裏的Action接口,實現Action接口的execute()方法。Action接口的代碼如下:
代碼 |
package com.opensymphony.xwork; import java.io.Serializable; public interface Action extends Serializable { public static final String SUCCESS = "success"; public static final String NONE = "none"; public static final String ERROR = "error"; public static final String INPUT = "input"; public static final String LOGIN = "login"; public String execute() throws Exception; } |
excute()方法是Action類裏最重要的部分,它執行返回String類型的值,在Action中返回的值一般使用它上面定義的標準靜態字符常量。例如:前面的HelloWorldAction返回的就是SUCCESS字符常量,真正的值當然就是“success”,它與xwork配置文件裏result標籤name的值是相對應的。它用來決定execute()方法執行完成之後,調用哪一種返回結果。字符常量的含義如下:
SUCCESS:Action正確的執行完成,返回相應的視圖;
NONE:表示Action正確的執行完成,但並不返回任何視圖;
ERROR:表示Action執行失敗,返回到錯誤處理視圖;
INPUT:Action的執行,需要從前端界面獲取參數,INPUT就是代表這個參數輸入的界面,一般在應用中,會對這些參數進行驗證,如果驗證沒有通過,將自動返回到該視圖;
LOGIN:Action因爲用戶沒有登陸的原因沒有正確執行,將返回該登陸視圖,要求用戶進行登陸驗證。
用戶註冊例子
下面我們將以一個用戶註冊的例子詳細介紹Action的原理:
功能描述:一個用戶註冊頁面register.jsp,用戶可以在這個頁面裏輸入用戶註冊的基本信息(例如:姓名、密碼、Email等),輸入完成提交表單,執行用戶註冊的Action,執行成功返回成功提示的頁面(register-result.jsp)並將註冊的信息輸出。
模型:User.java
控制:RegisterAction.java
視圖:register.jsp、register-result.jsp
配置:xwork.xml
User.java:
代碼 |
package register; public class User { private String username; private String password; private String email; private int age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } …… public int getAge() { return age; } public int setAge(int age) { this.age = age; } public String toString() { return "username=" + username + ";password=" + password + ";email=" + email + ";age=" + age; } } |
模型User是一個普通的JavaBean,它包含了用戶註冊的字段信息,並對每個字段提供相應的set和get方法。下面我們來看看進行用戶註冊動作的RegisterAction.java:
代碼 |
package example.register; import com.opensymphony.xwork.Action; /** * @author moxie-qac * [email protected] */ public class RegisterAction implements Action { private User user= new User(); public User getUser() { return this.user; } public String execute() { System.out.println("Start execute 。。。。。。。。。。。。。"); System.out.println("User="+user); //在這裏調用用戶註冊的業務邏輯,比如:將註冊信息存儲到數據庫 return SUCCESS; } } |
這個Action是不是特清爽?用戶註冊就這麼幾行代碼搞定,當然,我們提倡在Action裏最好不要實現業務代碼,Action的主要功能是提供從請求中取得參數的值,轉化成相應的模型,再將模型傳遞給執行業務操作的對象,比如:將註冊的用戶信息存儲到數據庫中,由業務對象執行業務操作,再返回執行的結果。爲了簡化我們省去了註冊的業務邏輯執行步驟。
再看看我們註冊信息輸入的頁面:register.jsp
代碼 |
<html> <head><title>Register Example</title></head> <body> <table border=0 width=97%> <tr><td align="left"> <form name="register" action="register.action" method="post"> Username:<input type="text" name="user.username"><br> Password:<input type="text" name="user.password"><br> Email:<input type="text" name="user.email"><br> Age:<input type="text" name="user.age"><br> <input type="submit" name="Submit"><br> </form> </td></tr> </table> </body> </html> |
register.jsp頁面其實只是一個普通的HTML頁面,它提供了一個表單,用來接受用戶輸入的註冊信息,它唯一特殊的部分就是input輸入框定義的name部分,例如:用戶姓名用的是“user. username”。這種命名方式代表什麼含義?它是必需的嗎?後面我們將會給出答案。
RegisterAction正確執行完成之後,會將執行的結果返回到register-result.jsp頁面,由它來顯示用戶在前面頁面輸入的註冊信息。register-result.jsp代碼如下:
代碼 |
<%@ taglib prefix="ww" uri="webwork" %> <html> <head><title>Register result</title></head> <body> <table border=0 width=97%> <tr> <td align="left"> Congratulation,your register success!<p> Username:<ww:property value="user.username"/><br> Password:<ww:property value="user.password"/><br> Email:<ww:property value="user.email"/><br> Age:<ww:property value="user.age"/><br> </td> </tr> </table> </body> </html> |
這個Jsp頁面使用了WebWork的標籤庫 <ww:property />,記得HelloWorld裏的greetings.jsp嗎?它也使用了這個標籤庫。我們看這個:<ww:property value="user.username"/>
它是一個普通的使用標籤庫語句,查看這個標籤庫的源程序,見包com.opensymphony.webwork.views.jsp裏的PropertyTag.java文件,你會發現這個類會根據value後面賦予的表達式值,去OgnlValueStack裏查找這個表達式值所對應的操作。執行這個語句OgnlValueStack會根據value的值(一個表達式)“user.username”去分別調用RegisterAction類的getUser()和User類的getUsername()方法,即:getUser().getUsername(),取得的數據就是前面註冊頁面輸入的用戶名。
我們把“user.username”這樣的語句叫做表達式語言(Expression Language,簡稱爲EL)。它由XWork框架提供,XWork表達式語言的核心是OGNL(Object Graph Notation Language),OGNL是一種功能強大,技術成熟,應用廣泛的表達式語言,將在下面的章節有詳細介紹。
我們在回到前面介紹的register.jsp,Input輸入框
<input type="text" name="user.username">裏用的“user.username”,現在我們可以明白,它不是隨意設置的,它是一個表達式語言,有着特殊的功能。看到這裏,不知道你心中是否有一個疑問:我們的RegisterAction是如何取得用戶註冊頁面輸入的數據呢?如果你做過Web開發,你一定會想到RegisterAction裏必需有一些從客戶端請求中獲取參數的語句,例如: 類似:String username = request.getParameter(“user. username”)的語句(request是HttpServletRequest的對象),去從request請求裏面獲取用戶輸入的參數值。可是我們這個Action裏面只有User對象簡單的get方法,並沒有其它的代碼。Xwork框架的Action是如何去實現了與Web無關?request請求的參數是怎麼傳遞到我們Action的模型User中呢?
在回答答案之前,我們先看一看Xwork的配置文件xwork.xml:
代碼 |
<action name="register" class="example.register.RegisterAction" > <result name="success" type="dispatcher"> <param name="location">/register-result.jsp</param> </result> <interceptor-ref name="params"/> </action> |
看了前面的介紹,這段配置文件應該不難理解。用戶通過註冊頁面register.jsp輸入自己的註冊信息,提交表單到動作register.action,它將有ServletDispatcher調度,從配置文件xwork.xml裏查找與“register”匹配的Action名字,即上面配置的Action。通過這個名字XWork框架找到這個Action的類:example.register.RegisterAction,XWork框架會負責去創建這個Action類的對象並調用execute()方法進行用戶註冊操作。正確執行execute()方法返回String類型數據“success”之後,它會請求再派遣到register-result.jsp頁面。
在這段配置文件裏,你一定注意到了它特殊的一句:<interceptor-ref name="params"/>,interceptor-ref標籤設置這個Action用到的攔截器(Interceptor),“params”引用的是配置文件中的<interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>,這個攔截器將在RegisterAction的execute()方法執行之前調用,作用是將request請求的參數值通過表達式語言設置到相應RegisterAction的模型裏。例如:register.jsp裏的<input type="text" name="user.username">,它輸入的值會由RegisterAction類的getUser()和User類的setUserName(“…”)設置到這個User模型裏。假設你在註冊頁面輸入用戶名“moxie”,提交表單ParametersInterceptor就會下面的操作:首先從請求中取得參數的名字和名字對應的值,分別爲:“user.username”和“moxie”,根據這個名字,從OgnlValueStack中取得堆棧最上面的getUser().setUsername(“moxie”)操作,即取得RegisterAction對象的User模型,並設置username屬性的值爲“moxie”。
原來,我們的Action是通過XWork的攔截器ParametersInterceptor從提交的表單中取得請求的參數和值,再通過OgnlValueStack來執行表達式,調用Action和模型裏相應的ge或set方法,將從請求中取得的值設置到模型中去。register.jsp中Input輸入框的name="user.username"是必需要遵守OGNL的命名規則。也正是很多攔截器的使用,使得我們的Action類和Web實現了完全的解耦,讓我們的Action能如此的簡單、優雅,攔截器的原理後面章節我們也將會有詳細的介紹。
羅索了這麼多,你一定是精通了這個用戶註冊的例子了吧!呵呵!
Action根據FormBean的不同可以分爲二類,
一類是Field-Driven(字段驅動的)Action
Action將直接用自己的字段來充當FormBean的功能,我們的例子就是使用這種方式。它一般用在頁面表單比較簡單的情況使用,而且可以直接用域對象作爲Action的字段,這樣就不用在另寫FormBean,減少了重複代碼。
另一類是Model-Driven(模型驅動的)Action
它很像Struts的FormBean,但在WebWork中,只要普通Java對象就可以充當模型部分。Model-Driven(模型驅動的)Action要求我們的Action實現com.opensymphony.xwork. ModelDriven接口,它有一個方法:Object getModel();,我們用這個方法返回我們的模型對象就可以了。
我們可以將前面的RegisterAction.java改爲Model-Driven(模型驅動的)Action:
代碼 |
package example.register; import com.opensymphony.xwork.Action; import com.opensymphony.xwork.ModelDriven; /** * @author moxie-qac * [email protected] * */ public class RegisterActionModel implements Action,ModelDriven{ private User user = new User(); public String execute() throws Exception { System.out.println("Start execute......。。。。。。。。。。。。。。"); System.out.println("User="+user); //在這裏調用用戶註冊的業務邏輯,比如:將註冊信息存儲到數據庫 return SUCCESS; } public Object getModel() { return user; } } |
這時我們輸入信息的頁面也有了變化:register-model.jsp
代碼 |
<html> <head><title>Register Example</title></head> <body> <table border=0 width=97%> <tr><td align="left"> <form name="register" action="registerModel.action" method="post"> Username:<input type="text" name="username"><br> Password:<input type="text" name="password"><br> Email:<input type="text" name="email"><br> Age:<input type="text" name="age"><br> <input type="submit" name="Submit"><br> </form> </td></tr> </table> </body> </html> |
我們發現,輸入框裏的命名發生了變化。它們都少了“user.”這部分信息。
當我們採用Model-Driven(模型驅動的)Action時,它將取得模型對象保存在值堆棧中。“name="username"”就是代表直接調用模型對象的setUsername()方法。
我們Action的在配置文件中,也要給它指定一個攔截器model-driven,它的作用就是將模型對象保存到值堆棧中。關於攔截器的介紹請看下面的章節。
配置文件如下:
代碼 |
<action name="registerModel" class="example.register.RegisterActionModel"> <result name="success" type="dispatcher"> <param name="location">/register-result-model.jsp</param> </result> <interceptor-ref name="model-driven"/> <interceptor-ref name="params"/> </action> |
ActionContext(Action上下文)
ActionContext介紹
通過上面用戶註冊例子的學習,我們知道Xwork與Web無關性,我們的Action不用去依賴於任何Web容器,不用和那些JavaServlet複雜的請求(Request)、響應(Response)關聯在一起。對請求(Request)的參數(Param),可以使用攔截器框架自動調用一些get()和set()方法設置到對應的Action的字段中。但是,僅僅取得請求參數的值就能完全滿足我們的功能要求嗎?不,在Web應用程序開發中,除了將請求參數自動設置到Action的字段中,我們往往也需要在Action裏直接獲取請求(Request)或會話(Session)的一些信息,甚至需要直接對JavaServlet Http的請求(HttpServletRequest)、響應(HttpServletResponse)操作。
帶着這些問題,我們來看看下面的一個功能需求:
我們需要在Action中取得request請求參數“username”的值:
代碼 |
ActionContext context = ActionContext.getContext(); Map params = context.getParameters(); String username = (String) params.get(“username”); |
爲了實現這個功能,我們用了三個步驟:
1、 取得我們當前的ActionContext對象context,ActionContext是個什麼鼕鼕?
2、 從context對象裏獲取我們所有的請求參數,取得的卻是一個Map對象params?
3、 居然可以從我們的Map對象params裏獲取我們需要的request請求參數“username”的值。
ActionContext(com.opensymphony.xwork.ActionContext)是Action執行時的上下文,上下文可以看作是一個容器(其實我們這裏的容器就是一個Map而已),它存放放的是Action在執行時需要用到的對象,比如:在使用WebWork時,我們的上下文放有請求的參數(Parameter)、會話(Session)、Servlet上下文(ServletContext)、本地化(Locale)信息等。
在每次執行Action之前都會創建新的ActionContext,ActionContext是線程安全的,也就是說在同一個線程裏ActionContext裏的屬性是唯一的,這樣我的Action就可以在多線程中使用。
我們可以通過ActionContext的靜態方法:ActionContext.getContext()來取得當前的ActionContext對象,我們看看這段代碼:
代碼 |
public static ActionContext getContext() { ActionContext context = (ActionContext) actionContext.get(); if (context == null) { OgnlValueStack vs = new OgnlValueStack(); context = new ActionContext(vs.getContext()); setContext(context); } return context; } |
一般情況,我們的ActionContext都是通過:ActionContext context = (ActionContext) actionContext.get();來獲取的。我們再來看看這裏的actionContext對象的創建:static ThreadLocal actionContext = new ActionContextThreadLocal();,ActionContextThreadLocal是實現ThreadLocal的一個內部類。ThreadLocal可以命名爲“線程局部變量”,它爲每一個使用該變量的線程都提供一個變量值的副本,使每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本衝突。這樣,我們ActionContext裏的屬性只會在對應的當前請求線程中可見,從而保證它是線程安全的。
下面我們看看怎麼通過ActionContext取得我們的HttpSession:
Map session = ActionContext.getContext().getSession();
原來我們取得的session卻是Map類型的對象,這是爲什麼?原來,我們的WebWork框架將與Web相關的很多對象重新進行了包裝,比如這裏就將HttpSession對象重新包裝成了一個Map對象,供我們的Action使用,而不用直接和底層的HttpSession打交道。也正是框架的包裝,讓我們的Actoion可以完全的和Web層解藕。
如果我們的Action需要直接與JavaServlet的HttpSession、HttpServletRequest等一些對象進行操作,我們又該如何處理?請看下面的ServletActionContext。
ServletActionContext
ServletActionContext(com.opensymphony.webwork. ServletActionContext),這個類直接繼承了我們上面介紹的ActionContext,它提供了直接與JavaServlet相關對象訪問的功能,它可以取得的對象有:
1、 javax.servlet.http.HttpServletRequest:HTTPservlet請求對象
2、 javax.servlet.http.HttpServletResponse;:HTTPservlet相應對象
3、 javax.servlet.ServletContext:Servlet 上下文信息
4、 javax.servlet.ServletConfig:Servlet配置對象
5、 javax.servlet.jsp.PageContext:Http頁面上下文
ServletActionContext除了提供了上面這些對象訪問,它當然也繼承了它父類ActionContex的很多功能,比如:對OgnlValueStack、Action名字等的訪問。
下面我們看看幾個簡單的例子,讓我們瞭解如何從ServletActionContext裏取得JavaServlet的相關對象:
1、 取得HttpServletRequest對象:
HttpServletRequest request = ServletActionContext. getRequest();
2、 取得HttpSession對象:
HttpSession session = ServletActionContext. getRequest().getSession();
ServletActionContext和ActionContext有着一些重複的功能,在我們的Action中,該如何去抉擇呢?我們遵循的原則是:如果ActionContext能夠實現我們的功能,那最好就不要使用ServletActionContext,讓我們的Action儘量不要直接去訪問JavaServlet的相關對象。在使用ActionContext時有一點要注意:不要在Action的構造函數裏使用ActionContext.getContext(),因爲這個時候ActionContext裏的一些值也許沒有設置,這時通過ActionContext取得的值也許是null。
ServletDispatcher是默認的處理Web Http請求的調度器,它是一個JavaServlet,是WebWork框架的控制器。所有對Action調用的請求都將通過這個ServletDispatcher調度。它將在web.xml裏配置ServletDispatcher時指定,讓所有對WebWork 的Action(默認的是.action的後綴)的請求都對應到該調度的JavaServlet中,具體配置在前面的WebWork安裝中有介紹。
ServletDispatcher接受客戶端的HTTP請求,將JavaServlet的很多相關對象進行包裝,再傳給我們的XWork框架,由我們的XWork框架去解析我們的xwork.xml配置文件,根據配置文件的信息,創建對應的Action,組裝並調用相應的攔截器,執行Action,返回執行結果。WebWork使用XWork的核心,主要是由這個ServletDispatcher去實現的,
ServletDispatcher的主要功能調用如下:
一、init()方法在服務器啓動時調用,
1、初始化Velocity引擎
2、檢查是否支持配置文件重新載入功能。如果webwork.configuration.xml.reload(見webwork.properties文件)設置爲true,每個request請求都將重新裝載xwork.xml配置文件。在開發環境使用將會非常方便,但在生產環境必需設置爲false。
代碼如下:
代碼 |
if ("true".equalsIgnoreCase(Configuration.getString("webwork.configuration.xml.reload"))) { FileManager.setReloadingConfigs(true); } |
3、設置一些文件上傳的信息,比如:上傳臨時目錄,上傳的最大字節等。都設置在webwork.properties文件裏,如果在classpath中找不到這個屬性文件,它會去讀取默認的default.properties
二、service()方法,每次客戶端的請求都將調用此方法。
1、通過request請求取得action的命名空間(namespace,與xwork.xml配置文件裏package標籤的name對應)
例如:/foo/bar/MyAction.action,取得的命名空間爲/foo/bar
在xwork.xml配置文件裏應該有這一段:
<package name="foo.bar" …….
2、根據servlet請求的Path,解析出要調用該請求的Action的名字(actionName),例如:(../foo/bar/MyAction.action -> MyAction)
在xwork.xml配置文件裏應該有:
<package name="foo.bar" …….
<Action name=” MyAction”……
3、 創建Action上下文(extraContext)。我們前面介紹的ActionContext上下文的對象,就是在這裏設置的。它將JavaServlet相關的對象進行包裝,放入到extraContext這個Map對象裏。
代碼 |
/** * 將所有的應用請求和servlet屬性保存到一個HashMap中, * @param requestMap 存放所有request請求屬性的Map * @param parameterMap 存放所有request請求參數的Map * @param sessionMap存放所有session屬性的Map * @param applicationMap 存放所有servlet上下文屬性的Map * @param request HttpServletRequest 對象 * @param response HttpServletResponse 對象. * @param servletConfig ServletConfig 對象. * @return代表Action 上下文的一個 HashMap */ public static HashMap createContextMap(Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response, ServletConfig servletConfig) { HashMap extraContext = new HashMap(); extraContext.put(ActionContext.PARAMETERS, parameterMap); extraContext.put(ActionContext.SESSION, sessionMap); extraContext.put(ActionContext.APPLICATION, applicationMap); extraContext.put(ActionContext.LOCALE, request.getLocale()); extraContext.put(HTTP_REQUEST, request); extraContext.put(HTTP_RESPONSE, response); extraContext.put(SERVLET_CONFIG, servletConfig); extraContext.put(COMPONENT_MANAGER, request.getAttribute("DefaultComponentManager")); // helpers to get access to request/session/application scope extraContext.put("request", requestMap); extraContext.put("session", sessionMap); extraContext.put("application", applicationMap); extraContext.put("parameters", parameterMap); AttributeMap attrMap = new AttributeMap(extraContext); extraContext.put("attr", attrMap); return extraContext; } |
下面我們來看看它是如何將request請求的參數和session進行包裝的:
代碼 |
protected Map getParameterMap(HttpServletRequest request) throws IOException { return request.getParameterMap(); } |
這個方法比較簡單,它直接調用了HttpServletRequest的方法getParameterMap(),將所有request請求的參數封裝到一個Map中。
代碼 |
protected Map getSessionMap(HttpServletRequest request) { return new SessionMap(request); } |
這個方法取得所有Session中的屬性,它調用了com.opensymphony.webwork.dispatcher. SessionMap類,這個類實現了Map接口,在entrySet()方法中列舉Session的所有屬性,存放在Set中。
4、根據前面獲得的namespace、actionName、extraContext,創建一個ActonProxy
代碼 |
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext); |
默認的proxy是com.opensymphony.xwork.DefaultActionProxy,在它的構造函數會進行下面的操作:
1)、根據namespace、actionName讀取xwork.xml配置文件裏這個Action的所有配置信息。
2)、創建ActionInvocation
代碼 |
invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext); |
默認的invocation是com.opensymphony.xwork.DefaultActionInvocation,它的構造函數操作有:
a) 由com.opensymphony.xwork.ObjectFactory創建我們配置文件描述的Action對象。再將這個Action對象存放入OgnlValueStack中。記得我們前面用戶註冊的例子嗎?當用戶提交表達時它會有表達式語言向OgnlValueStack取得Action對象的字段,再把輸入框的數據設置到對應的Action字段中,這個Action對象就是在這個時候進棧的。
b) 傳入extraContext參數,創建與ActionInvocation對應的Action上下文(ActionContext)。記得我們在介紹ActionContext的最後,提出了一個需要注意的地方:不要在Action構造函數中調用ActionContext.getContext()。現在應該能明白,原來是Action對象實例在ActionContext對象實例之前創建的,所有這樣取得ActionContext容器對象就有可能會返回null
c) 取得這個Action對應的所有攔截器(Interceptor),存放入java.util.Iterator對象中。
5、執行proxy的execute()方法,這個方法最核心的語句是:retCode = invocation.invoke();, invocation對象的invoke()方法它遍歷並執行這個Action對應的所有攔截器,執行Action對應的方法(默認的是execute()),根據Action執行返回的值去調用執行相應的Result(返回結果處理)的方法。