WebWork介紹WebWork是由OpenSymphony組織開發(fā)的,致力于組件化和代碼重用的拉出式MVC模式J2EE Web框架。WebWork目前最新版本是2.1,現(xiàn)在的WebWork2.x前身是Rickard Oberg開發(fā)的WebWork,但現(xiàn)在WebWork已經(jīng)被拆分成了Xwork1和WebWork2兩個(gè)項(xiàng)目,如下示意圖所示:
Xwork簡潔、靈活功能強(qiáng)大,它是一個(gè)標(biāo)準(zhǔn)的Command模式實(shí)現(xiàn),并且完全從web層脫離出來。Xwork提供了很多核心功能:前端攔截機(jī)(interceptor),運(yùn)行時(shí)表單屬性驗(yàn)證,類型轉(zhuǎn)換,強(qiáng)大的表達(dá)式語言(OGNL – the Object Graph Notation Language),IoC(Inversion of Control倒置控制)容器等。 WebWork2建立在Xwork之上,處理HTTP的響應(yīng)和請(qǐng)求。WebWork2使用ServletDispatcher將HTTP請(qǐng)求的變成Action(業(yè)務(wù)層Action類), session(會(huì)話)application(應(yīng)用程序)范圍的映射,request請(qǐng)求參數(shù)映射。WebWork2支持多視圖表示,視圖部分可以使用JSP, Velocity, FreeMarker, JasperReports,XML等。 下面我們提到的WebWork將為WebWork2,使用的版本是2.1。
WebWork安裝-HelloWorldWebWork安裝當(dāng)然,在具體開發(fā)使用介紹之前,搭建好運(yùn)行環(huán)境是必備的。 首先從https://webwork.dev./servlets/ProjectDocumentList下載最新的WebWork壓縮包,并將其解壓開來。打開解壓目錄,你將看到以下的文件和目錄: webwork-2.x.jar 當(dāng)然就是WebWrok最新發(fā)布的Jar包 webwork-example.war 是WebWrok自帶的很有代表性的功能演示例子,掌握它是提高你的WebWork技術(shù)水平的捷徑 webwork-migration.jar 提供快速將1.x版本移植到2.x版本所用的類文件 docs目錄 WebWrok的使用文檔,包括api文檔、clover文檔、單元測(cè)試(Junit)文檔等 lib目錄 WebWork在運(yùn)行或編譯時(shí)所用到的所有.jar包 src目錄 源程序目錄 2、WebWork是J2EE Web框架,當(dāng)然要運(yùn)行在Web容器中,我用的是穩(wěn)定的Tomcat 4.1,關(guān)于tomcat的安裝和部署請(qǐng)自己搞定。 3、用WebWork當(dāng)然要將它的運(yùn)行時(shí)用到的Jar包放到Web容器可以找到的ClassPath中,將步驟1介紹的webwork-2.x.jar放到你部署目錄下WEB-INF\lib目錄里,同時(shí)將WebWrok解壓目錄lib\core下的所有.jar文件也拷貝到WEB-INF\lib目錄,這些是運(yùn)行WebWork必需要用到的jar包。 4、了解Web框架的朋友都知道,一般Web框架都是通過一個(gè)JavaServlet控制器提供統(tǒng)一的請(qǐng)求入口,解析請(qǐng)求的url,再去調(diào)用相應(yīng)的Action進(jìn)行業(yè)務(wù)處理。WebWork也不例外,它要求你在web.xml文件里配置一個(gè)派遣器ServletDispatcher,它初始化WebWrok的一些配置信息,解析XWork的Action配置信息,根據(jù)請(qǐng)求去組裝和調(diào)用執(zhí)行相應(yīng)的攔截器(Interceptor)、Action、Action Result(Action執(zhí)行結(jié)果的輸出)等,具體配置如下: …… <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結(jié)尾的所有url請(qǐng)求將直接有ServletDispatcher去調(diào)度。下面我們寫一個(gè)經(jīng)典的HelloWorld,跑一個(gè)簡單實(shí)例來驗(yàn)證你運(yùn)行環(huán)境是否可用,并感受一下簡單、功能強(qiáng)大的WebWork的開發(fā)。 注意:如果使用WebWork自帶的標(biāo)簽庫,除了配置相應(yīng)的標(biāo)簽庫以外,還須將com.opensymphony.webwork.views.velocity.WebWorkVelocityServlet配置到web.xml,具體可以參考webwork-example里面的配置。 HelloWorld首先看下面這個(gè)程序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是一個(gè)普通的Java類,它實(shí)現(xiàn)了Action這個(gè)接口。Action是一個(gè)非常簡單的接口,只有一個(gè)方法:public String execute() throws Exception; ,Action類介紹見下一節(jié)。HelloWorldAction有一個(gè)String類型字段greeting,在execute()方法中,greeting被賦值“Hello World!”,并返回String型常量SUCCESS,SUCCESS的定義詳見Action接口,這個(gè)常量代表了execute()方法執(zhí)行成功,將返回成功頁面。 返回的頁面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自帶的標(biāo)簽庫。它的作用是輸出變量“greeting”的值。這個(gè)<ww:property value="greeting"/>語句,相當(dāng)于調(diào)用相應(yīng)Action(HelloWorldAction)的getGreeting()方法,取得變量“greeting”的值。 我們的HelloWorld代碼就這么多,完了??墒?,HelloWorldAction怎么去調(diào)用、執(zhí)行?執(zhí)行成功它又怎么知道返回到greetings.jsp?XWork的配置文件xwork.xml會(huì)負(fù)責(zé)將要執(zhí)行的Action和展現(xiàn)的視圖連接起來,見xwork.xml的如下片斷: <action name="hello" class=" helloWorld .HelloWorldAction"> <result name="success" type="dispatcher"> <param name="location">/greetings.jsp</param> </result> </action> 我們先看action標(biāo)簽:name=”hello”,表示我們調(diào)用這個(gè)Action的標(biāo)識(shí)是hello,這樣我們可以通過下面的url訪問這個(gè)Action:…/hello.action, 代碼寫完,剩下的當(dāng)然是編譯、部署。啟動(dòng)tomcat服務(wù)器之后我們就可以執(zhí)行了: 在瀏覽器里輸入你的地址:http://localhost:8080/webwork/hello.action 你將會(huì)看到如下結(jié)果: Action(動(dòng)作)Action介紹Action在MVC模式中擔(dān)任控制部分的角色,在WebWork中使用的最多。每個(gè)請(qǐng)求的動(dòng)作都對(duì)應(yīng)于一個(gè)相應(yīng)的Action,一個(gè)Action是一個(gè)獨(dú)立的工作單元和控制命令,它必需要實(shí)現(xiàn)XWork里的Action接口,實(shí)現(xiàn)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類里最重要的部分,它執(zhí)行返回String類型的值,在Action中返回的值一般使用它上面定義的標(biāo)準(zhǔn)靜態(tài)字符常量。例如:前面的HelloWorldAction返回的就是SUCCESS字符常量,真正的值當(dāng)然就是“success”,它與xwork配置文件里result標(biāo)簽name的值是相對(duì)應(yīng)的。它用來決定execute()方法執(zhí)行完成之后,調(diào)用哪一種返回結(jié)果。字符常量的含義如下: SUCCESS:Action正確的執(zhí)行完成,返回相應(yīng)的視圖; NONE:表示Action正確的執(zhí)行完成,但并不返回任何視圖; ERROR:表示Action執(zhí)行失敗,返回到錯(cuò)誤處理視圖; INPUT:Action的執(zhí)行,需要從前端界面獲取參數(shù),INPUT就是代表這個(gè)參數(shù)輸入的界面,一般在應(yīng)用中,會(huì)對(duì)這些參數(shù)進(jìn)行驗(yàn)證,如果驗(yàn)證沒有通過,將自動(dòng)返回到該視圖; LOGIN:Action因?yàn)橛脩魶]有登陸的原因沒有正確執(zhí)行,將返回該登陸視圖,要求用戶進(jìn)行登陸驗(yàn)證。 用戶注冊(cè)例子下面我們將以一個(gè)用戶注冊(cè)的例子詳細(xì)介紹Action的原理: 功能描述:一個(gè)用戶注冊(cè)頁面register.jsp,用戶可以在這個(gè)頁面里輸入用戶注冊(cè)的基本信息(例如:姓名、密碼、Email等),輸入完成提交表單,執(zhí)行用戶注冊(cè)的Action,執(zhí)行成功返回成功提示的頁面(register-result.jsp)并將注冊(cè)的信息輸出。 模型: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是一個(gè)普通的JavaBean,它包含了用戶注冊(cè)的字段信息,并對(duì)每個(gè)字段提供相應(yīng)的set和get方法。下面我們來看看進(jìn)行用戶注冊(cè)動(dòng)作的RegisterAction.java: package example.register;
import com.opensymphony.xwork.Action;
/** * @author moxie-qac * achqian@yahoo.com.cn */ 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); //在這里調(diào)用用戶注冊(cè)的業(yè)務(wù)邏輯,比如:將注冊(cè)信息存儲(chǔ)到數(shù)據(jù)庫
return SUCCESS; } } 這個(gè)Action是不是特清爽?用戶注冊(cè)就這么幾行代碼搞定,當(dāng)然,我們提倡在Action里最好不要實(shí)現(xiàn)業(yè)務(wù)代碼,Action的主要功能是提供從請(qǐng)求中取得參數(shù)的值,轉(zhuǎn)化成相應(yīng)的模型,再將模型傳遞給執(zhí)行業(yè)務(wù)操作的對(duì)象,比如:將注冊(cè)的用戶信息存儲(chǔ)到數(shù)據(jù)庫中,由業(yè)務(wù)對(duì)象執(zhí)行業(yè)務(wù)操作,再返回執(zhí)行的結(jié)果。為了簡化我們省去了注冊(cè)的業(yè)務(wù)邏輯執(zhí)行步驟。 再看看我們注冊(cè)信息輸入的頁面: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頁面其實(shí)只是一個(gè)普通的HTML頁面,它提供了一個(gè)表單,用來接受用戶輸入的注冊(cè)信息,它唯一特殊的部分就是input輸入框定義的name部分,例如:用戶姓名用的是“user. username”。這種命名方式代表什么含義?它是必需的嗎?后面我們將會(huì)給出答案。 RegisterAction正確執(zhí)行完成之后,會(huì)將執(zhí)行的結(jié)果返回到register-result.jsp頁面,由它來顯示用戶在前面頁面輸入的注冊(cè)信息。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> 這個(gè)Jsp頁面使用了WebWork的標(biāo)簽庫 <ww:property />,記得HelloWorld里的greetings.jsp嗎?它也使用了這個(gè)標(biāo)簽庫。我們看這個(gè):<ww:property value="user.username"/> 我們把“user.username”這樣的語句叫做表達(dá)式語言(Expression Language,簡稱為EL)。它由XWork框架提供,XWork表達(dá)式語言的核心是OGNL(Object Graph Notation Language),OGNL是一種功能強(qiáng)大,技術(shù)成熟,應(yīng)用廣泛的表達(dá)式語言,將在下面的章節(jié)有詳細(xì)介紹。 我們?cè)诨氐角懊娼榻B的register.jsp,Input輸入框 在回答答案之前,我們先看一看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> 看了前面的介紹,這段配置文件應(yīng)該不難理解。用戶通過注冊(cè)頁面register.jsp輸入自己的注冊(cè)信息,提交表單到動(dòng)作register.action,它將有ServletDispatcher調(diào)度,從配置文件xwork.xml里查找與“register”匹配的Action名字,即上面配置的Action。通過這個(gè)名字XWork框架找到這個(gè)Action的類:example.register.RegisterAction,XWork框架會(huì)負(fù)責(zé)去創(chuàng)建這個(gè)Action類的對(duì)象并調(diào)用execute()方法進(jìn)行用戶注冊(cè)操作。正確執(zhí)行execute()方法返回String類型數(shù)據(jù)“success”之后,它會(huì)請(qǐng)求再派遣到register-result.jsp頁面。 在這段配置文件里,你一定注意到了它特殊的一句:<interceptor-ref name="params"/>,interceptor-ref標(biāo)簽設(shè)置這個(gè)Action用到的攔截器(Interceptor),“params”引用的是配置文件中的<interceptor name="params" class=" 原來,我們的Action是通過XWork的攔截器ParametersInterceptor從提交的表單中取得請(qǐng)求的參數(shù)和值,再通過OgnlValueStack來執(zhí)行表達(dá)式,調(diào)用Action和模型里相應(yīng)的ge或set方法,將從請(qǐng)求中取得的值設(shè)置到模型中去。register.jsp中Input輸入框的name="user.username"是必需要遵守OGNL的命名規(guī)則。也正是很多攔截器的使用,使得我們的Action類和Web實(shí)現(xiàn)了完全的解耦,讓我們的Action能如此的簡單、優(yōu)雅,攔截器的原理后面章節(jié)我們也將會(huì)有詳細(xì)的介紹。 羅索了這么多,你一定是精通了這個(gè)用戶注冊(cè)的例子了吧!呵呵! Field-Driven Action vs. Model-Driven ActionAction根據(jù)FormBean的不同可以分為二類, 一類是Field-Driven(字段驅(qū)動(dòng)的)Action Action將直接用自己的字段來充當(dāng)FormBean的功能,我們的例子就是使用這種方式。它一般用在頁面表單比較簡單的情況使用,而且可以直接用域?qū)ο笞鳛?/span>Action的字段,這樣就不用在另寫FormBean,減少了重復(fù)代碼。 另一類是Model-Driven(模型驅(qū)動(dòng)的)Action 它很像Struts的FormBean,但在WebWork中,只要普通Java對(duì)象就可以充當(dāng)模型部分。Model-Driven(模型驅(qū)動(dòng)的)Action要求我們的Action實(shí)現(xiàn)com.opensymphony.xwork. ModelDriven接口,它有一個(gè)方法:Object getModel();,我們用這個(gè)方法返回我們的模型對(duì)象就可以了。 我們可以將前面的RegisterAction.java改為Model-Driven(模型驅(qū)動(dòng)的)Action: package example.register;
import com.opensymphony.xwork.Action; import com.opensymphony.xwork.ModelDriven;
/** * @author moxie-qac * achqian@yahoo.com.cn * */ 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); //在這里調(diào)用用戶注冊(cè)的業(yè)務(wù)邏輯,比如:將注冊(cè)信息存儲(chǔ)到數(shù)據(jù)庫
return SUCCESS; }
public Object getModel() { return user; } } 這時(shí)我們輸入信息的頁面也有了變化: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> 我們發(fā)現(xiàn),輸入框里的命名發(fā)生了變化。它們都少了“user.”這部分信息。 當(dāng)我們采用Model-Driven(模型驅(qū)動(dòng)的)Action時(shí),它將取得模型對(duì)象保存在值堆棧中?!?/span>name="username"”就是代表直接調(diào)用模型對(duì)象的setUsername()方法。 我們Action的在配置文件中,也要給它指定一個(gè)攔截器model-driven,它的作用就是將模型對(duì)象保存到值堆棧中。關(guān)于攔截器的介紹請(qǐng)看下面的章節(jié)。 配置文件如下: <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介紹通過上面用戶注冊(cè)例子的學(xué)習(xí),我們知道Xwork與Web無關(guān)性,我們的Action不用去依賴于任何Web容器,不用和那些JavaServlet復(fù)雜的請(qǐng)求(Request)、響應(yīng)(Response)關(guān)聯(lián)在一起。對(duì)請(qǐng)求(Request)的參數(shù)(Param),可以使用攔截器框架自動(dòng)調(diào)用一些get()和set()方法設(shè)置到對(duì)應(yīng)的Action的字段中。但是,僅僅取得請(qǐng)求參數(shù)的值就能完全滿足我們的功能要求嗎?不,在Web應(yīng)用程序開發(fā)中,除了將請(qǐng)求參數(shù)自動(dòng)設(shè)置到Action的字段中,我們往往也需要在Action里直接獲取請(qǐng)求(Request)或會(huì)話(Session)的一些信息,甚至需要直接對(duì)JavaServlet Http的請(qǐng)求(HttpServletRequest)、響應(yīng)(HttpServletResponse)操作。 帶著這些問題,我們來看看下面的一個(gè)功能需求: 我們需要在Action中取得request請(qǐng)求參數(shù)“username”的值: ActionContext context = ActionContext.getContext(); Map params = context.getParameters(); String username = (String) params.get(“username”); 為了實(shí)現(xiàn)這個(gè)功能,我們用了三個(gè)步驟: 1、 取得我們當(dāng)前的ActionContext對(duì)象context,ActionContext是個(gè)什么冬冬? 2、 從context對(duì)象里獲取我們所有的請(qǐng)求參數(shù),取得的卻是一個(gè)Map對(duì)象params? 3、 居然可以從我們的Map對(duì)象params里獲取我們需要的request請(qǐng)求參數(shù)“username”的值。 ActionContext(com.opensymphony.xwork.ActionContext)是Action執(zhí)行時(shí)的上下文,上下文可以看作是一個(gè)容器(其實(shí)我們這里的容器就是一個(gè)Map而已),它存放放的是Action在執(zhí)行時(shí)需要用到的對(duì)象,比如:在使用WebWork時(shí),我們的上下文放有請(qǐng)求的參數(shù)(Parameter)、會(huì)話(Session)、Servlet上下文(ServletContext)、本地化(Locale)信息等。 在每次執(zhí)行Action之前都會(huì)創(chuàng)建新的ActionContext,ActionContext是線程安全的,也就是說在同一個(gè)線程里ActionContext里的屬性是唯一的,這樣我的Action就可以在多線程中使用。 我們可以通過ActionContext的靜態(tài)方法:ActionContext.getContext()來取得當(dāng)前的ActionContext對(duì)象,我們看看這段代碼: 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();來獲取的。我們?cè)賮砜纯催@里的actionContext對(duì)象的創(chuàng)建:static ThreadLocal actionContext = new ActionContextThreadLocal();,ActionContextThreadLocal是實(shí)現(xiàn)ThreadLocal的一個(gè)內(nèi)部類。ThreadLocal可以命名為“線程局部變量”,它為每一個(gè)使用該變量的線程都提供一個(gè)變量值的副本,使每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)和其它線程的副本沖突。這樣,我們ActionContext里的屬性只會(huì)在對(duì)應(yīng)的當(dāng)前請(qǐng)求線程中可見,從而保證它是線程安全的。 下面我們看看怎么通過ActionContext取得我們的HttpSession: Map session = ActionContext.getContext().getSession(); 原來我們?nèi)〉玫?/span>session卻是Map類型的對(duì)象,這是為什么?原來,我們的WebWork框架將與Web相關(guān)的很多對(duì)象重新進(jìn)行了包裝,比如這里就將HttpSession對(duì)象重新包裝成了一個(gè)Map對(duì)象,供我們的Action使用,而不用直接和底層的HttpSession打交道。也正是框架的包裝,讓我們的Actoion可以完全的和Web層解藕。 如果我們的Action需要直接與JavaServlet的HttpSession、HttpServletRequest等一些對(duì)象進(jìn)行操作,我們又該如何處理?請(qǐng)看下面的ServletActionContext。 ServletActionContextServletActionContext(com.opensymphony.webwork. ServletActionContext),這個(gè)類直接繼承了我們上面介紹的ActionContext,它提供了直接與JavaServlet相關(guān)對(duì)象訪問的功能,它可以取得的對(duì)象有: 1、 javax.servlet.http.HttpServletRequest:HTTPservlet請(qǐng)求對(duì)象 2、 javax.servlet.http.HttpServletResponse;:HTTPservlet相應(yīng)對(duì)象 3、 javax.servlet.ServletContext:Servlet 上下文信息 4、 javax.servlet.ServletConfig:Servlet配置對(duì)象 5、 javax.servlet.jsp.PageContext:Http頁面上下文 ServletActionContext除了提供了上面這些對(duì)象訪問,它當(dāng)然也繼承了它父類ActionContex的很多功能,比如:對(duì)OgnlValueStack、Action名字等的訪問。 下面我們看看幾個(gè)簡單的例子,讓我們了解如何從ServletActionContext里取得JavaServlet的相關(guān)對(duì)象: 1、 取得HttpServletRequest對(duì)象: HttpServletRequest request = ServletActionContext. getRequest(); 2、 取得HttpSession對(duì)象: HttpSession session = ServletActionContext. getRequest().getSession(); ServletActionContext和ActionContext有著一些重復(fù)的功能,在我們的Action中,該如何去抉擇呢?我們遵循的原則是:如果ActionContext能夠?qū)崿F(xiàn)我們的功能,那最好就不要使用ServletActionContext,讓我們的Action盡量不要直接去訪問JavaServlet的相關(guān)對(duì)象。在使用ActionContext時(shí)有一點(diǎn)要注意:不要在Action的構(gòu)造函數(shù)里使用ActionContext.getContext(),因?yàn)檫@個(gè)時(shí)候ActionContext里的一些值也許沒有設(shè)置,這時(shí)通過ActionContext取得的值也許是null。
ServletDispatcher原理ServletDispatcher是默認(rèn)的處理Web Http請(qǐng)求的調(diào)度器,它是一個(gè)JavaServlet,是WebWork框架的控制器。所有對(duì)Action調(diào)用的請(qǐng)求都將通過這個(gè)ServletDispatcher調(diào)度。它將在web.xml里配置ServletDispatcher時(shí)指定,讓所有對(duì)WebWork 的Action(默認(rèn)的是.action的后綴)的請(qǐng)求都對(duì)應(yīng)到該調(diào)度的JavaServlet中,具體配置在前面的WebWork安裝中有介紹。 ServletDispatcher接受客戶端的HTTP請(qǐng)求,將JavaServlet的很多相關(guān)對(duì)象進(jìn)行包裝,再傳給我們的XWork框架,由我們的XWork框架去解析我們的xwork.xml配置文件,根據(jù)配置文件的信息,創(chuàng)建對(duì)應(yīng)的Action,組裝并調(diào)用相應(yīng)的攔截器,執(zhí)行Action,返回執(zhí)行結(jié)果。WebWork使用XWork的核心,主要是由這個(gè)ServletDispatcher去實(shí)現(xiàn)的,
ServletDispatcher的主要功能調(diào)用如下: 一、init()方法在服務(wù)器啟動(dòng)時(shí)調(diào)用, 1、初始化Velocity引擎 2、檢查是否支持配置文件重新載入功能。如果webwork.configuration.xml.reload(見webwork.properties文件)設(shè)置為true,每個(gè)request請(qǐng)求都將重新裝載xwork.xml配置文件。在開發(fā)環(huán)境使用將會(huì)非常方便,但在生產(chǎn)環(huán)境必需設(shè)置為false。 代碼如下: if ("true".equalsIgnoreCase(Configuration.getString("webwork.configuration.xml.reload"))) { FileManager.setReloadingConfigs(true); } 3、設(shè)置一些文件上傳的信息,比如:上傳臨時(shí)目錄,上傳的最大字節(jié)等。都設(shè)置在webwork.properties文件里,如果在classpath中找不到這個(gè)屬性文件,它會(huì)去讀取默認(rèn)的default.properties
二、service()方法,每次客戶端的請(qǐng)求都將調(diào)用此方法。 1、通過request請(qǐng)求取得action的命名空間(namespace,與xwork.xml配置文件里package標(biāo)簽的name對(duì)應(yīng)) 例如:/foo/bar/MyAction.action,取得的命名空間為/foo/bar 在xwork.xml配置文件里應(yīng)該有這一段: <package name="foo.bar" ……. 2、根據(jù)servlet請(qǐng)求的Path,解析出要調(diào)用該請(qǐng)求的Action的名字(actionName),例如:(../foo/bar/MyAction.action -> MyAction) 在xwork.xml配置文件里應(yīng)該有: <package name="foo.bar" ……. <Action name=” MyAction”……
3、 創(chuàng)建Action上下文(extraContext)。我們前面介紹的ActionContext上下文的對(duì)象,就是在這里設(shè)置的。它將JavaServlet相關(guān)的對(duì)象進(jìn)行包裝,放入到extraContext這個(gè)Map對(duì)象里。 /** * 將所有的應(yīng)用請(qǐng)求和servlet屬性保存到一個(gè)HashMap中, * @param requestMap 存放所有request請(qǐng)求屬性的Map * @param parameterMap 存放所有request請(qǐng)求參數(shù)的Map * @param sessionMap存放所有session屬性的Map * @param applicationMap 存放所有servlet上下文屬性的Map * @param request HttpServletRequest 對(duì)象 * @param response HttpServletResponse 對(duì)象. * @param servletConfig ServletConfig 對(duì)象. * @return代表Action 上下文的一個(gè) 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請(qǐng)求的參數(shù)和session進(jìn)行包裝的: protected Map getParameterMap(HttpServletRequest request) throws IOException { return request.getParameterMap(); } 這個(gè)方法比較簡單,它直接調(diào)用了HttpServletRequest的方法getParameterMap(),將所有request請(qǐng)求的參數(shù)封裝到一個(gè)Map中。
protected Map getSessionMap(HttpServletRequest request) { return new SessionMap(request); } 這個(gè)方法取得所有Session中的屬性,它調(diào)用了com.opensymphony.webwork.dispatcher. SessionMap類,這個(gè)類實(shí)現(xiàn)了Map接口,在entrySet()方法中列舉Session的所有屬性,存放在Set中。
4、根據(jù)前面獲得的namespace、actionName、extraContext,創(chuàng)建一個(gè)ActonProxy ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext); 默認(rèn)的proxy是com.opensymphony.xwork.DefaultActionProxy,在它的構(gòu)造函數(shù)會(huì)進(jìn)行下面的操作:1)、根據(jù)namespace、actionName讀取xwork.xml配置文件里這個(gè)Action的所有配置信息。 2)、創(chuàng)建ActionInvocation invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext); 默認(rèn)的invocation是com.opensymphony.xwork.DefaultActionInvocation,它的構(gòu)造函數(shù)操作有: a) 由com.opensymphony.xwork.ObjectFactory創(chuàng)建我們配置文件描述的Action對(duì)象。再將這個(gè)Action對(duì)象存放入OgnlValueStack中。記得我們前面用戶注冊(cè)的例子嗎?當(dāng)用戶提交表達(dá)時(shí)它會(huì)有表達(dá)式語言向OgnlValueStack取得Action對(duì)象的字段,再把輸入框的數(shù)據(jù)設(shè)置到對(duì)應(yīng)的Action字段中,這個(gè)Action對(duì)象就是在這個(gè)時(shí)候進(jìn)棧的。 b) 傳入extraContext參數(shù),創(chuàng)建與ActionInvocation對(duì)應(yīng)的Action上下文(ActionContext)。記得我們?cè)诮榻BActionContext的最后,提出了一個(gè)需要注意的地方:不要在Action構(gòu)造函數(shù)中調(diào)用ActionContext.getContext()?,F(xiàn)在應(yīng)該能明白,原來是Action對(duì)象實(shí)例在ActionContext對(duì)象實(shí)例之前創(chuàng)建的,所有這樣取得ActionContext容器對(duì)象就有可能會(huì)返回null c) 取得這個(gè)Action對(duì)應(yīng)的所有攔截器(Interceptor),存放入java.util.Iterator對(duì)象中。
5、執(zhí)行proxy的execute()方法,這個(gè)方法最核心的語句是:retCode = invocation.invoke();, invocation對(duì)象的invoke()方法它遍歷并執(zhí)行這個(gè)Action對(duì)應(yīng)的所有攔截器,執(zhí)行Action對(duì)應(yīng)的方法(默認(rèn)的是execute()),根據(jù)Action執(zhí)行返回的值去調(diào)用執(zhí)行相應(yīng)的Result(返回結(jié)果處理)的方法。 Action的單元測(cè)試理解了ServletDispatcher,我們就明白了整個(gè)框架調(diào)用執(zhí)行的順序。Action雖然是與Web無關(guān),可是它的創(chuàng)建、參數(shù)設(shè)置、執(zhí)行與我們的WebWork、XWork緊密關(guān)聯(lián)在一起,有我們的控制器ServletDispatcher去統(tǒng)一調(diào)度,那我們?nèi)绾稳?duì)Action進(jìn)行獨(dú)立的單元測(cè)試呢? 請(qǐng)看下面的例子:使用單元測(cè)試框架JUnit對(duì)register.User. RegisterAction做單元測(cè)試 見example.register. RegisterActionTest類testExecuteWithProxyFactory()方法:
public void testExecuteWithProxyFactory() throws Exception{
Map params = new HashMap(); params.put("user.username","Moxie"); params.put("user.password","mypassword"); params.put("user.email","achqian@yahoo.com.cn"); params.put("user.age",new Integer(23)); Map extraContext = new HashMap(); extraContext.put(ActionContext.PARAMETERS,params);
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("example", "register", extraContext); proxy.setExecuteResult(false); assertEquals(proxy.execute(),"success");
RegisterAction action = (RegisterAction) proxy.getAction(); assertEquals(action.getUser().getUsername(),"Moxie"); assertEquals(action.getUser().getAge(),23); } 下面解說這個(gè)方法: 1、 對(duì)象params表示請(qǐng)求參數(shù)的Map,在它里面設(shè)置了注冊(cè)用戶的信息。extraContext當(dāng)然就是我們ActionContext上下文的容器,它里面保存了放置請(qǐng)求參數(shù)的對(duì)象params 2、 創(chuàng)建我們的ActionProxy,它傳入的參數(shù)有:“example”-這個(gè)Action的命名空間,“register”-Action對(duì)應(yīng)的名字,extraContext-存放Actin上下文里的對(duì)象,,執(zhí)行并將它返回的值與“success”比較,測(cè)試Action是否能正確執(zhí)行完成。注意:proxy.setExecuteResult(false);,因?yàn)槲覀兪菃卧獪y(cè)試,所以Action執(zhí)行完成就可以了,不用再去調(diào)用結(jié)果響應(yīng)的操作,故將是否執(zhí)行結(jié)果設(shè)置為“false”。 3、 Action正確執(zhí)行完成之后,我們也可以測(cè)試現(xiàn)在Action的字段里的數(shù)據(jù)是否按照我們預(yù)期的要求正確設(shè)置。從ActionProxy對(duì)象里取得執(zhí)行的Action,即RegisterAction對(duì)象,再取得它的User模型,將其數(shù)據(jù)與前面設(shè)置參數(shù)的數(shù)據(jù)進(jìn)行比較,判斷它是否等于我們預(yù)期設(shè)置的數(shù)值。
Result Type前面我們學(xué)習(xí)了ServletDispatcher,它是WebWork框架機(jī)制的核心。它和Action在我們MVC模式中,扮演著控制器的角色,MVC模式通過控制器實(shí)現(xiàn)了我們模型和視圖的分離。WebWork提供了多種活靈活視圖展現(xiàn)方式。 我們先看看前面用戶注冊(cè)例子的展現(xiàn)方式:我們使用的是Jsp和WebWork自帶的標(biāo)簽庫,Action對(duì)應(yīng)的視圖當(dāng)然是在xwork.xml配置文件里設(shè)置: <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> Result是Action執(zhí)行完返回的一個(gè)字符串常量,它表示Action執(zhí)行完成的狀態(tài),比如:執(zhí)行成功、執(zhí)行失敗等。在我們前面Action的介紹中,詳細(xì)介紹了它默認(rèn)的標(biāo)準(zhǔn)Result,當(dāng)然Result我們也可以自己定義,只要是一個(gè)字符串常量就可以了。 Result的值在xwork.xml配置文件里就是result標(biāo)簽里“name”的值,name="success"表示Action執(zhí)行成功,返回“success”就對(duì)應(yīng)此標(biāo)簽的配置,進(jìn)行視圖輸出。 “type”就是我們的Result Type,Result Type是一個(gè)類,它在Action執(zhí)行完成并返回Result之后,決定采用哪一種視圖技術(shù),將執(zhí)行結(jié)果展現(xiàn)給用戶。我們輸出的類型是:type="dispatcher",它對(duì)應(yīng)com.opensymphony.webwork.dispatcher.ServletDispatcherResult這個(gè)類,它將執(zhí)行結(jié)果通過javax.servlet.RequestDispatcher的forward()或include()方法調(diào)度到Jsp頁面展現(xiàn)。 我們可以自己開發(fā)Result Type,實(shí)現(xiàn)我們需要的視圖展現(xiàn)方式。Result Type必需要實(shí)現(xiàn)com.opensymphony.xwork..Result接口。在WebWork中,它已經(jīng)為我們提供了很多Result Type,實(shí)現(xiàn)了視圖部分對(duì)JSP, Velocity, FreeMarker, JasperReports,XML等的支持,具體如下表格:
Dispatcher:通過javax.servlet.RequestDispatcher的forward()或include()方法調(diào)度到頁面展現(xiàn),這樣的頁面一般是Jsp頁面。
例子: <result name="success" type="dispatcher"> <param name="location">register-result.jsp</param> </result> 也可以簡單寫成這樣: <result name="success" type="dispatcher">register-result.jsp</result>
Redirect:將響應(yīng)重定向到瀏覽器指定的位置,它將會(huì)導(dǎo)致Action執(zhí)行完成的數(shù)據(jù)丟失或不再可用。它在程序里是通過調(diào)用javax.servlet.http.HttpServletResponse.sendRedirect(String location)方法,將響應(yīng)定向到參數(shù)location指定的、新的url中。
例子 <result name="success" type="redirect"> <param name="location">foo.jsp</param> <param name="parse">false</param> </result>
Action Chaining:一種特殊的視圖結(jié)果,將Action執(zhí)行完之后鏈接到另一個(gè)Action中繼續(xù)執(zhí)行。新的Action使用上一個(gè)Action的上下文(ActionContext)。
例子: <result name="success" type="chain"> <param name="actionName">bar</param> <param name="namespace">/foo</param> </result>
將要調(diào)用的Action如下: <action name="bar" class="myPackage.barAction"> ... </action>
Velocity:它類似Jsp的執(zhí)行環(huán)境(使用JavaServlet容器),將Velocity模板轉(zhuǎn)化成數(shù)據(jù)流的形式,直接通過JavaServlet輸出。
例子: <result name="success" type="velocity"> <param name="location">foo.vm</param> </result>
FreeMarker:FreeMarker是一個(gè)純Java模板引擎;一個(gè)普通的基于模板生成文本的工具,它只能應(yīng)用在Web應(yīng)用環(huán)境中。
例子: <result name="success" type="freemarker">foo.ftl</result>
JasperReports:將Action執(zhí)行的結(jié)果通過JasperReports報(bào)表形式輸出,可以指定JasperReports支持的輸出格式(PDF、HTML、XLS、CSV、XML等),默認(rèn)是通過PDF格式輸出。
例子: <result name="success" type="jasper"> <param name="location">foo.jasper</param> <param name="dataSource">mySource</param> <param name="format">CSV</param> </result>
或者默認(rèn)的pdf格式 <result name="success" type="jasper"> <param name="location">foo.jasper</param> <param name="dataSource">mySource</param> </result>
XML/XSL:將結(jié)果轉(zhuǎn)換為xml輸出
例子: <result name="success" type="xslt">foo.xslt</result>
表達(dá)式與言EL和OGNLOGNL介紹OGNL是Object-Graph Navigation Language的縮寫,它是一種功能強(qiáng)大的表達(dá)式語言(Expression Language,簡稱為EL),通過它簡單一致的表達(dá)式語法,可以存取對(duì)象的任意屬性,調(diào)用對(duì)象的方法,遍歷整個(gè)對(duì)象的結(jié)構(gòu)圖,實(shí)現(xiàn)字段類型轉(zhuǎn)化等功能。它使用相同的表達(dá)式去存取對(duì)象的屬性。 XWork遵循“不要重復(fù)地發(fā)明同一個(gè)輪子”的理論,它的表達(dá)式語言核心用的就是這個(gè)OGNL。我們先來看看一個(gè)簡單的例子: 還記得我們用戶注冊(cè)的那個(gè)例子嗎?我們輸入框的name用到的名字就是OGNL的表達(dá)式,比如:用戶名的輸入框:“<input type="text" name="user.username">”,在用戶注冊(cè)成功之后我們要顯示用戶注冊(cè)的信息,用了“<ww:property value="user.username"/>”。Input輸入框里的“user.username”,它解析成Java語句為:getUser().setUsername();,property標(biāo)簽里的“user.username”解析為Java語句:getUser.getUsername();。 我們的兩個(gè)表達(dá)式都是相同的,但前一個(gè)保存對(duì)象屬性的值,后一個(gè)是取得對(duì)象屬性的值。表達(dá)式語言簡單、易懂卻又功能強(qiáng)大,關(guān)于OGNL更多的介紹可以去http://www.,那里有很詳細(xì)的文檔。 值堆棧-OgnlValueStackOGNL在框架中的應(yīng)用,最主要是支持我們的值堆棧(Value Stack)——OgnlValueStack,它主要的功能是通過表達(dá)式語言來存取對(duì)象的屬性。用戶界面輸入數(shù)據(jù),它會(huì)根據(jù)保存表達(dá)式將數(shù)據(jù)依次保存到它堆棧的對(duì)象中,業(yè)務(wù)操作完成,結(jié)果數(shù)據(jù)會(huì)通過表達(dá)式被獲取、輸出。 還記得我們用戶注冊(cè)的例子嗎?下面我們用一段程序來演示它向OgnlValueStack中保存、取得數(shù)據(jù)的步驟: // DemoRegisterValueStack package example.register;
import com.opensymphony.xwork.util.OgnlValueStack;
/** * @author moxie-qac * achqian@yahoo.com.cn * */ public class DemoRegisterValueStack { public void demo(){ RegisterAction action = new RegisterAction(); OgnlValueStack valueStack= new OgnlValueStack(); valueStack.push(action);
valueStack.setValue("user.username","Moxie"); System.out.println("username = "+valueStack.findValue("user.username")); }
public static void main(String[] args) { DemoRegisterValueStack demoValueStack = new DemoRegisterValueStack(); demoValueStack.demo(); } } 我們來看一看它的demo()方法: 1、 創(chuàng)建我們的Action(RegisterAction)類的對(duì)象action,將action對(duì)象壓入堆棧valueStack中。在WebWrok中Action的創(chuàng)建、入棧是在DefaultActionInvocation構(gòu)造函數(shù)中進(jìn)行的,詳細(xì)介紹見:ServletDispatcher原理。 2、 通過表達(dá)式語言,調(diào)用堆棧對(duì)象的get()、set()方法,設(shè)置該對(duì)象的值。 public void setValue(String expr, Object value) 語句:valueStack.setValue("user.username","Moxie"); 的作用等同于:action.getUser().setUsername("Moxie"); 3、 通過表達(dá)式語言,去堆棧對(duì)象中查找我們前面保存的值,并在控制臺(tái)打印。valueStack.findValue("user.username")等同與語句: 最后控制臺(tái)打印的結(jié)果: username = Moxie CompoundRoot 在OgnlValueStack中,一個(gè)堆棧其實(shí)是一個(gè)List。查看OgnlValueStack你會(huì)發(fā)現(xiàn),堆棧就是com.opensymphony.xwork.util.CompoundRoot類的對(duì)象: public class CompoundRoot extends ArrayList { //~ Constructors ///////////////////////////////////// public CompoundRoot() { } public CompoundRoot(List list) { super(list); } //~ Methods //////////////////////////////////////////// public CompoundRoot cutStack(int index) { return new CompoundRoot(subList(index, size())); } public Object peek() { return get(0); } public Object pop() { return remove(0); } public void push(Object o) { add(0, o); } } 我們通過表達(dá)式向堆棧對(duì)象操作時(shí),我們并不知道堆棧中有哪些對(duì)象。OgnlValueStack會(huì)根據(jù)堆棧由上向下的順序(先入棧在下面,最后入棧在最上面)依次去查找與表達(dá)式匹配的對(duì)象方法,找到即進(jìn)行相應(yīng)的存取操作。假設(shè)后面對(duì)象也有相同的方法,將不會(huì)被調(diào)用。 下面我們看一個(gè)對(duì)OgnlValueStack操作的程序,它主要演示了如何對(duì)Map對(duì)象的存取和OgnlValueStack堆棧的原理: /* * Created on 2004-6-15 * DemoGroupValueStack.java */ package example.register;
import com.opensymphony.xwork.util.OgnlValueStack;
/** * @author moxie-qac * achqian@yahoo.com.cn * */ public class DemoGroupValueStack {
public void demoAction(){ DemoGroupAction action = new DemoGroupAction(); OgnlValueStack valueStack= new OgnlValueStack(); valueStack.push(action);
User zhao = new User(); zhao.setUsername("zhao"); zhao.setEmail("zhao@yahoo.com.cn");
User qian = new User(); qian.setUsername("qian"); qian.setEmail("qian@yahoo.com.cn");
valueStack.setValue("users[‘zhao‘]",zhao); valueStack.setValue("users[‘qian‘]",qian);
System.out.println("users[‘zhao‘] = "+valueStack.findValue("users[‘zhao‘]")); System.out.println("users[‘qian‘] = "+valueStack.findValue("users[‘qian‘]")); System.out.println("users size = "+valueStack.findValue("users.size"));
System.out.println("allUserName[0] = "+valueStack.findValue("allUserName[0]")); }
public void demoModels(){
User model_a = new User(); model_a.setUsername("model_a"); User model_b = new User(); model_b.setUsername("model_b"); User model_c = new User(); model_c.setUsername("model_c");
OgnlValueStack valueStack= new OgnlValueStack(); valueStack.push(model_a); valueStack.push(model_b); valueStack.push(model_c);
System.out.println("username = "+valueStack.findValue("username")); System.out.println("[1].username = "+valueStack.findValue("[1].username")); System.out.println("[0].toString = "+valueStack.findValue("[0]")); System.out.println("[1].toString = "+valueStack.findValue("[1]")); System.out.println("[2].toString = "+valueStack.findValue("[2]"));
} public static void main(String[] args) { DemoGroupValueStack demoValueStack = new DemoGroupValueStack(); demoValueStack.demoAction(); demoValueStack.demoModels(); } }
/* * Created on 2004-6-15 * DemoAction.java */ package example.register;
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
/** * @author moxie-qac * achqian@yahoo.com.cn * */ public class DemoGroupAction {
private Map users = new HashMap();
public Map getUsers(){ return this.users; }
public List getAllUserName(){ return new ArrayList(users.keySet()); } public String execute(){ //執(zhí)行業(yè)務(wù)操作 return null; } public String toString(){ return users.toString(); } } 注意:1、Map屬性的存取,它的表達(dá)式語言如:users[‘zhao‘],注意它用’’來引用HashMap的key字符串。 2、demoModels()方法演示了OgnlValueStack中堆棧的原理,請(qǐng)?zhí)貏e注意它的[0].toString、[1].toString、[2].toString,它們依次調(diào)用堆棧中對(duì)象的toString()方法,并逐一的減少堆棧最上面的對(duì)象。 控制臺(tái)輸出的結(jié)果如下: users[‘zhao‘] = username=zhao;password=null;email=zhao@yahoo.com.cn;age=0 users[‘qian‘] = username=qian;password=null;email=qian@yahoo.com.cn;age=0 users size = 2 allUserName[0] = qian
username = model_c [1].username = model_b [0].toString = [username=model_c;password=null;email=null;age=0, username=model_b;password=null;email=null;age=0, username=model_a;password=null;email=null;age=0] [1].toString = [username=model_b;password=null;email=null;age=0, username=model_a;password=null;email=null;age=0] [2].toString = [username=model_a;password=null;email=null;age=0]
Interceptor(攔截器)框架Interceptor(攔截器)將Action共用的行為獨(dú)立出來,在Action執(zhí)行前后運(yùn)行。這也就是我們所說的AOP(Aspect Oriented Programming,面向切面編程),它是分散關(guān)注的編程方法,它將通用需求功能從不相關(guān)類之中分離出來;同時(shí),能夠使得很多類共享一個(gè)行為,一旦行為發(fā)生變化,不必修改很多類,只要修改這個(gè)行為就可以。 Interceptor將很多功能從我們的Action中獨(dú)立出來,大量減少了我們Action的代碼,獨(dú)立出來的行為具有很好的重用性。XWork、WebWork的許多功能都是有Interceptor實(shí)現(xiàn),可以在配置文件中組裝Action用到的Interceptor,它會(huì)按照你指定的順序,在Action執(zhí)行前后運(yùn)行。Interceptor在框架中的應(yīng)用如下圖所示:
當(dāng)你提交對(duì)Aciton(默認(rèn)是.action結(jié)尾的Url)的請(qǐng)求時(shí),ServletDispatcher會(huì)根據(jù)你的請(qǐng)求,去調(diào)度并執(zhí)行相應(yīng)的Action。在Action執(zhí)行之前,調(diào)用被 Interceptor截取,Interceptor在Action執(zhí)行前后運(yùn)行。 我們?cè)谟脩糇?cè)的例子中就使用了取得Request請(qǐng)求參數(shù)的攔截器,配置文件中<interceptor-ref name="params"/>將攔截器params組裝到RegisterAction中?!?/span>params”在我們的webwork-default.xml配置文件中有定義,webwork-default.xml中攔截器的定義如下: <interceptors> <interceptor name="timer" class="com.opensymphony.xwork.interceptor.TimerInterceptor"/> <interceptor name="logger" class="com.opensymphony.xwork.interceptor.LoggingInterceptor"/> <interceptor name="chain" class="com.opensymphony.xwork.interceptor.ChainingInterceptor"/> <interceptor name="static-params" class="com.opensymphony.xwork.interceptor.StaticParametersInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/> <interceptor name="model-driven" class="com.opensymphony.xwork.interceptor.ModelDrivenInterceptor"/> <interceptor name="component" class="com.opensymphony.xwork.interceptor.component.ComponentInterceptor"/> <interceptor name="token" class="com.opensymphony.webwork.interceptor.TokenInterceptor"/> <interceptor name="token-session" class="com.opensymphony.webwork.interceptor.TokenSessionStoreInterceptor"/> <interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/> <interceptor name="workflow" class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor"/> <interceptor name="servlet-config" class="com.opensymphony.webwork.interceptor.ServletConfigInterceptor"/> <interceptor name="prepare" class="com.opensymphony.xwork.interceptor.PrepareInterceptor"/> <interceptor name="conversionError" class="com.opensymphony.webwork.interceptor.WebWorkConversionErrorInterceptor"/> <interceptor-stack name="defaultStack"> <interceptor-ref name="static-params"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> </interceptor-stack> <interceptor-stack name="validationWorkflowStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> </interceptors> 這些都時(shí)有框架提供的默認(rèn)的Interceptor,下面我來看看Interceptor使用的步驟: 1、 創(chuàng)建一個(gè)自己需要的Interceptor類,它必需實(shí)現(xiàn) 2、 在配置文件(xwork..xml)中申明這個(gè)Interceptor類,它放在標(biāo)簽<interceptor />中,同是<interceptor />標(biāo)簽嵌入在<interceptors />標(biāo)簽內(nèi)部。 3、 創(chuàng)建Interceptor棧,使用標(biāo)簽:<interceptor-stack />,讓一組Interceptor可以按次序調(diào)用。(可選) 4、 指定Action所要用到的Interceptor(前面申明過的),可以用<interceptor-ref />或<default-interceptor-ref />標(biāo)簽。前面的標(biāo)簽指定某個(gè)Action所用到的Interceptor,如果Action沒有被用<interceptor-ref />指定Interceptor,它將使用<default-interceptor-ref />指定的Interceptor。 框架中給我們提供了很多實(shí)用的Interceptor,它的定義上面已經(jīng)給出,它的具體功能如下: l timer:記錄Action執(zhí)行的時(shí)間,并做為日志信息輸出; l logger:在日志信息中輸出要執(zhí)行的Action信息; l chain:將前一個(gè)執(zhí)行結(jié)束的Action屬性設(shè)置到當(dāng)前的Action中。它被用在ResultType為“chain”指定結(jié)果的Action中,該結(jié)果Action對(duì)象會(huì)從OgnlValueStack中獲得前一個(gè)Action對(duì)應(yīng)的屬性,它實(shí)現(xiàn)Action鏈之間的數(shù)據(jù)傳遞; l static-params:將xwork.xml配置文件里定義的Action參數(shù),設(shè)置到對(duì)應(yīng)的Action中。Action參數(shù)使用<param />標(biāo)簽,是<action />標(biāo)簽的直接子元素。我們這里定義的Action類必需實(shí)現(xiàn)com.opensymphony.xwork.config.entities. Parameterizable接口; l params:將Request請(qǐng)求的參數(shù)設(shè)置到相應(yīng)Action對(duì)象的屬性中,用戶注冊(cè)例子用到過這個(gè)攔截器; l model-driven:如果Action實(shí)現(xiàn)ModelDriven接口,它將getModel()取得的模型對(duì)象存入OgnlValueStack中; l component:激活組件功能支持,讓注冊(cè)過的組件在當(dāng)前Action中可用,即為Action提供IoC(依賴倒轉(zhuǎn)控制)框架的支持; l token:核對(duì)當(dāng)前Action請(qǐng)求(request)的有效標(biāo)識(shí),防止重復(fù)提交Action請(qǐng)求(request)。 l token-session:功能同上,但是當(dāng)提交無效的Action請(qǐng)求標(biāo)識(shí)時(shí),它會(huì)將請(qǐng)求數(shù)據(jù)保存到session中。 l validation:實(shí)現(xiàn)使用xml配置文件({Action}-validation.xml)對(duì)Action屬性值進(jìn)行驗(yàn)證,詳細(xì)請(qǐng)看后面介紹的驗(yàn)證框架。 l workflow:調(diào)用Action類的驗(yàn)證功能,假設(shè)Action使用ValidationAware實(shí)現(xiàn)驗(yàn)證(ActionSupport提供此功能),如果驗(yàn)證沒有通過,workflow會(huì)將請(qǐng)求返回到input視圖(Action的<result />中定義的)。 l servlet-config:提供Action直接對(duì)HttpServletRequest或HttpServletResponse等JavaServlet api的訪問,Action要實(shí)現(xiàn)相應(yīng)的接口,例如:ServletRequestAware或ServletResponseAware等。如果必需要提供對(duì)JavaServlet api的訪問,我們建議使用ServletActionContext,在前面ActionContext章節(jié)中有介紹。 l prepare:在Action執(zhí)行之前調(diào)用Action的prepare()方法,這個(gè)方法是用來準(zhǔn)備Action執(zhí)行之前要做的工作。它要求我們的Action必需實(shí)現(xiàn)com.opensymphony.xwork. Preparable接口 l conversionError:用來處理框架進(jìn)行類型轉(zhuǎn)化(Type Conversion)時(shí)的出錯(cuò)信息。它將存儲(chǔ)在ActionContext中的類型轉(zhuǎn)化(Type Conversion)錯(cuò)誤信息轉(zhuǎn)化成相應(yīng)的Action字段的錯(cuò)誤信息,保存在堆棧中。根據(jù)需要,可以將這些錯(cuò)誤信息在視圖中顯示出來。 Interceptor的原理下面我們來看看Interceptor是如何實(shí)現(xiàn)在Action執(zhí)行前后調(diào)用的: Action和Interceptor在框架中的執(zhí)行,是由ActionInvocation對(duì)象調(diào)用的。它是用方法:String invoke() throws Exception;來實(shí)現(xiàn)的,它首先會(huì)依次調(diào)用Action對(duì)應(yīng)的Interceptor,執(zhí)行完成所有的Interceptor之后,再去調(diào)用Action的方法,代碼如下: if (interceptors.hasNext()) { Interceptor interceptor = (Interceptor) interceptors.next(); resultCode = interceptor.intercept(this); } else { if (proxy.getConfig().getMethodName() == null) { resultCode = getAction().execute(); } else { resultCode = invokeAction(getAction(), proxy.getConfig()); } } 它會(huì)在攔截器棧中遍歷Interceptor,調(diào)用Interceptor的方法: 我們一直都提到,Interceptor是在Action前后執(zhí)行,可是從上面的代碼我們看到的卻是執(zhí)行完所有Interceptor的intercept()方法之后再去調(diào)用我們的Action?!霸?/span>Action前后執(zhí)行”是如何實(shí)現(xiàn)的呢?我們來看看抽象類AroundInterceptor的intercept()實(shí)現(xiàn): public String intercept(ActionInvocation invocation) throws Exception { String result = null;
before(invocation); result = invocation.invoke(); after(invocation, result);
return result; } 原來在intercept()方法又對(duì)ActionInvocation的invoke()方法進(jìn)行遞歸調(diào)用,ActionInvocation循環(huán)嵌套在intercept()中,一直到語句result = invocation.invoke();執(zhí)行結(jié)束,即:Action執(zhí)行完并返回結(jié)果result,這時(shí)Interceptor對(duì)象會(huì)按照剛開始執(zhí)行的逆向順序依次執(zhí)行結(jié)束。這樣before()方法將在Action執(zhí)行前調(diào)用,after()方法在Action執(zhí)行之后運(yùn)行。 驗(yàn)證框架WebWork提供了在Action執(zhí)行之前,對(duì)輸入數(shù)據(jù)的驗(yàn)證功能,它使用了其核心XWork的驗(yàn)證框架。提供了如下功能: 1、 可配置的驗(yàn)證文件。它的驗(yàn)證文件是一個(gè)獨(dú)立的XML配置文件,對(duì)驗(yàn)證的添加、修改只需更改配置文件,無需編譯任何的Class。 2、 驗(yàn)證文件和被驗(yàn)證的對(duì)象完全解藕。驗(yàn)證對(duì)象是普通的JavaBean就可以了(可以是FormBean、域?qū)ο蟮龋?,它們不需?shí)現(xiàn)任何額外的方法或繼承額外的類。 3、 多種不同的驗(yàn)證方式。因?yàn)樗?yàn)證功能是可以繼承的,所以可以用多種不同的方式指定驗(yàn)證文件,比如:通過父類的Action、通過Action、通過Action的方法、通過Action所使用的對(duì)象,等等。 4、 強(qiáng)大的表達(dá)式驗(yàn)證。它使用了OGNL的表達(dá)式語言,提供強(qiáng)大的表達(dá)式驗(yàn)證功能。 5、 同時(shí)支持服務(wù)器端和客戶端驗(yàn)證。 為用戶注冊(cè)添加驗(yàn)證功能下面我們來看看如何為用戶注冊(cè)添加驗(yàn)證功能: 1、 注冊(cè)我們的驗(yàn)證類型 WebWork為不同的驗(yàn)證要求提供不同的驗(yàn)證類型。一個(gè)驗(yàn)證類型,一般是有一個(gè)類來提供。這個(gè)類必須實(shí)現(xiàn)接口:com.opensymphony.xwork.validator.Validator,但我們?cè)趯懽约旱尿?yàn)證類型時(shí),無需直接實(shí)現(xiàn)Validator接口,它有抽象類可供直接使用如ValidatorSupport、FieldValidatorSupport等。 驗(yàn)證類型在使用之前,必須要在ValidatorFactory(com.opensymphony.xwork.validator. ValidatorFactory)中注冊(cè)。可以有二種方法實(shí)現(xiàn)驗(yàn)證類型的注冊(cè)。一、寫程序代碼進(jìn)行注冊(cè),它使用ValidatorFactory類的靜態(tài)方法:registerValidator(String name, String className)。二、使用配置文件validators.xml進(jìn)行注冊(cè),要求把文件validators.xml放到ClassPath的跟目錄中(/WEB-INF/classes)。但在實(shí)際開發(fā)中,一般都使用第二中注冊(cè)方法。我們的驗(yàn)證類型注冊(cè)如下: <validators> <validator name="required" class="com.opensymphony.xwork.validator.validators.RequiredFieldValidator"/> <validator name="requiredstring" class="com.opensymphony.xwork.validator.validators.RequiredStringValidator"/> <validator name="int" class="com.opensymphony.xwork.validator.validators.IntRangeFieldValidator"/> <validator name="date" class="com.opensymphony.xwork.validator.validators.DateRangeFieldValidator"/> <validator name="expression" class="com.opensymphony.xwork.validator.validators.ExpressionValidator"/> <validator name="fieldexpression" class="com.opensymphony.xwork.validator.validators.FieldExpressionValidator"/> <validator name="email" class="com.opensymphony.xwork.validator.validators.EmailValidator"/> <validator name="url" class="com.opensymphony.xwork.validator.validators.URLValidator"/> <validator name="visitor" class="com.opensymphony.xwork.validator.validators.VisitorFieldValidator"/> <validator name="conversion" class="com.opensymphony.xwork.validator.validators.ConversionErrorFieldValidator"/> <validator name="stringlength" class="com.opensymphony.xwork.validator.validators.StringLengthFieldValidator"/> </validators> 注冊(cè)驗(yàn)證類型的配置文件非常簡單。它使用標(biāo)簽<validator>提供名-值對(duì)的形式注冊(cè)。這樣我們的驗(yàn)證文件就可以直接引用它的名字。 2、 開啟Action的驗(yàn)證功能 如果Action要使用驗(yàn)證框架的驗(yàn)證功能,它必須在配置文件中指定攔截器“validation”,它的定義如下: <interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>。 我們的驗(yàn)證文件必須以ActionName-validation.xml格式命名,它必須被放置到與這個(gè)Action相同的包中。你也可以為這個(gè)Action通過別名的方式指定驗(yàn)證文件,它的命名格式為:ActionName-aliasname-validation.xml?!?/span>ActionName ”是我們Action的類名;“aliasname”是我們?cè)谂渲梦募?/span>xwork.xml)中定義這個(gè)Action所用到的名稱。這樣,同一個(gè)Action類,在配置文件中的不同定義就可以對(duì)應(yīng)不同的驗(yàn)證文件。驗(yàn)證框架也會(huì)根據(jù)Action的繼承結(jié)構(gòu)去查找Action的父類驗(yàn)證文件,如果找到它會(huì)去執(zhí)行這個(gè)父類的驗(yàn)證。
3、 實(shí)現(xiàn)我們的驗(yàn)證文件:RegisterActionSupport-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www./xwork/xwork-validator-1.0.dtd"> <validators> <field name="user.username"> <field-validator type="requiredstring"> <message>You must enter a value for username.</message> </field-validator> </field> <field name="user.password"> <field-validator type="requiredstring"> <message>You must enter a value for password.</message> </field-validator> <field-validator type="fieldexpression"> <param name="expression">user.password == verifyPassword</param> <message>Passwords don‘t match.</message> </field-validator> </field> <field name="user.email"> <field-validator type="email"> <message>You must enter a valid email.</message> </field-validator> </field> <field name="user.age"> <field-validator type="int"> <param name="min">6</param> <param name="max">100</param> <message>Age must be between ${min} and ${max}, current value is ${user.age}.</message> </field-validator> </field> </validators> 說明: 1)、<field>標(biāo)簽代表一個(gè)字段,它的屬性“name”和頁面輸入框的“name”屬性必需完全一致,其實(shí)它也就是我們的表達(dá)式語言。 2)、<field-validator>標(biāo)簽定義我們的驗(yàn)證規(guī)則,type屬性的值就是就是我們前面定義的驗(yàn)證類型。 3)、驗(yàn)證文件中,字段的數(shù)據(jù)是通過表達(dá)式語言從我們的值堆棧(OgnlValueStack)中取得,一般是Action或Model對(duì)象。例如:我們的字段“user.age”,它會(huì)通過Action的getUser().getAge()來取得用戶輸入的年齡,再來根據(jù)驗(yàn)證的類型“int”和最大值最小值的參數(shù)來判斷輸入的數(shù)據(jù)是否能通過驗(yàn)證。 4)、不管驗(yàn)證是否通過,我們的Action都會(huì)執(zhí)行,但如果驗(yàn)證沒有通過,它不會(huì)調(diào)用Action的execute()方法。
4、 顯示Action的驗(yàn)證錯(cuò)誤信息 如果用戶輸入的數(shù)據(jù)驗(yàn)證沒有通過,我們需重新返回輸入頁面,并給出錯(cuò)誤信息提示。攔截器棧“validationWorkflowStack”為我們實(shí)現(xiàn)了這個(gè)功能。它首先驗(yàn)證用戶輸入的數(shù)據(jù),如果驗(yàn)證沒有通過將不執(zhí)行我們Action的execute()方法,而是將請(qǐng)求重新返回到輸入頁面。 我們的xwork.xml配置文件如下: <action name="registerSupport" class="example.register.RegisterActionSupport" > <result name="success" type="dispatcher"> <param name="location">/register-result.jsp</param> </result> <result name="input" type="dispatcher"> <param name="location">/registerSupport.jsp</param> </result> <interceptor-ref name="validationWorkflowStack"/> </action>
通過接口ValidationAware,我們可以獲得類級(jí)別或字段級(jí)別的驗(yàn)證錯(cuò)誤信息,這個(gè)錯(cuò)誤信息也就是我們驗(yàn)證文件中<message>標(biāo)簽里的數(shù)據(jù)。ActionSupport類已實(shí)現(xiàn)了此接口,這樣在應(yīng)用中我們的Action只要繼承ActionSupport類就可以了。RegisterActionSupport.java代碼如下: package example.register;
import com.opensymphony.xwork.ActionSupport;
/** * @author moxie-qac * achqian@yahoo.com.cn * */ public class RegisterActionSupport extends ActionSupport {
private User user= new User(); private String verifyPassword;
public User getUser(){ return this.user; }
public String execute(){ //在這里調(diào)用用戶注冊(cè)的業(yè)務(wù)邏輯,比如:將注冊(cè)信息存儲(chǔ)到數(shù)據(jù)庫 return SUCCESS; }
public String getVerifyPassword(){ return this.verifyPassword; }
public void setVerifyPassword(String verPassword){ this.verifyPassword = verPassword; } } 我們WebWork的UI標(biāo)簽庫直接提供了驗(yàn)證錯(cuò)誤信息顯示功能。如果字段級(jí)別的驗(yàn)證沒有通過,它會(huì)在輸入框上方顯示驗(yàn)證文件定義的錯(cuò)誤提示信息。我們將用戶輸入的頁面更改如下: registerSupport.jsp <%@ taglib uri="webwork" prefix="ww" %> <html> <head><title>Register Example</title></head> <body> <table border=0 width=97%> <tr><td align="left"> <ww:form name="‘test‘" action="‘/example/registerSupport.action‘" method="‘POST‘"> <ww:textfield label="‘Username‘" name="‘user.username‘" required="true"/> <ww:textfield label="‘Password‘" name="‘user.password‘" required="true"/> <ww:textfield label="‘VerifyPassword‘" name="‘verifyPassword‘" required="true"/> <ww:textfield label="‘Email‘" name="‘user.email‘" required="true"/> <ww:textfield label="‘Age‘" name="‘user.age‘" required="true"/> <ww:submit value="‘Submit‘"/> </ww:form> </td></tr> </table> </body> </html> 我們上面的例子使用的是服務(wù)器端驗(yàn)證。WebWork也為我們提供了方便的客戶端驗(yàn)證。它將驗(yàn)證自動(dòng)生成JavaScript腳本。如果要使用客戶端驗(yàn)證只需改變相應(yīng)的驗(yàn)證類型就可以了(輸入頁面的表單必需使用<ww:form>標(biāo)簽,并設(shè)置屬性“validate="true"”)。具體的驗(yàn)證類型可以在WebWork的包com.opensymphony.webwork.validators中找到。
XWork配置詳述XWork配置文件是以“xwork”命名的.xml文件,它必需放到類路徑(classPath)的根目錄, Web應(yīng)用一般放在classes目錄中,它需要遵守DTD的規(guī)范(現(xiàn)在是xwork-1.0.dtd)。這個(gè)文件定義了我們的Action,Interceptor,Result的配置和相互之間的映射。下面我們看看用戶注冊(cè)的完整XWork配置文件: <!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www./xwork/xwork-1.0.dtd">
<xwork>
<include file="webwork-default.xml"/>
<package name="example" extends="webwork-default"> <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>
<action name="registersupport" class="example.register.RegisterActionSupport" > <result name="success" type="dispatcher"> <param name="location">/register-result.jsp</param> </result> <result name="input" type="dispatcher"> <param name="location">/registerSupport.jsp</param> </result> <interceptor-ref name="validationWorkflowStack"/> </action>
</package>
</xwork> xwork.xml文件的標(biāo)簽元素Xwork:xwork配置文件的所有內(nèi)容,都是定義在<xwork>標(biāo)簽中,它的直接子標(biāo)簽有<package>和<include>。 Package:我們的Action,Interceptor,Result都是在此標(biāo)簽中定義。<package>標(biāo)簽有一個(gè)必需的屬性“name”,它用來標(biāo)識(shí)唯一的一個(gè)package。屬性“extends”是可選的,它用來繼承前面定義的一個(gè)或一個(gè)以上package配置信息,包括所有的interceptor、interceptor-stack和action的配置信息。注意,配置文件按文檔的順序,由上向下執(zhí)行,因此,用“extends”引用的package必需在引用之前定義。屬性“sbstract”是可選的,它用來設(shè)置package為抽象的package,它可以被繼承同時(shí)它的Action配置信息在運(yùn)行時(shí)將不可見。 屬性namespace也是可選的,它用來分隔不同package定義的action,讓這些action處于不同的命名空間(namespaces)。這樣,我們不同的package可以有相同的action命名,因?yàn)榭梢酝ㄟ^命名空間來區(qū)分。如果不指定namespace,默認(rèn)的是空字符串。命名空間也可以被用在安全控制方面,它可以根據(jù)不同的命名空間指定不同的訪問權(quán)限。
Result-type:用來定義輸出結(jié)果類型的Class,它用簡單的名-值對(duì)來定義。當(dāng)然,我們自己寫的輸出結(jié)果類型也必需在這里定義。例如: <result-type name="dispatcher" class="com.opensymphony.webwork.dispatcher.ServletDispatcherResult" default="true"/>,default="true"表示如果在Action的result中不指定result-type,就使用這個(gè)默認(rèn)的result-type。
Interceptors:它是一個(gè)簡單的<interceptors> <interceptors/>標(biāo)簽,我們的interceptor和interceptor-stack都在此標(biāo)簽內(nèi)定義。
Interceptor:當(dāng)然,就是用來定義我們的攔截器。它的定義非常簡單,名-值對(duì)的形式。例如:<interceptor name="timer" class="com.opensymphony.xwork.interceptor.TimerInterceptor"/>。在action中,可以通過<interceptor-ref />來直接引用前面定義了的攔截器。
Interceptor-stack:用來將上面定義的interceptor組織成堆棧的形式,這樣我們就可以創(chuàng)建一組標(biāo)準(zhǔn)的interceptor,讓它按照順序執(zhí)行。在我們的Action中直接引用這個(gè)interceptor堆棧就可以了,不用逐個(gè)interceptor去引用。 例如: <interceptor-stack name="validationWorkflowStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> Interceptor Param:我們的interceptor是在ActionConfig級(jí)別被實(shí)例化和存儲(chǔ)的,也就是說一個(gè)Action引用的每個(gè)interceptor都會(huì)有相應(yīng)的實(shí)例。這樣,我們?cè)诙x和引用interceptor的時(shí)候都可以為它設(shè)置相應(yīng)的參數(shù)值。例如: <interceptor name="test" class="com.opensymphony.xwork.TestInterceptor"> <param name="foo">expectedFoo</param> </interceptor> 在Action或Interceptor-stack中引用時(shí)也可以設(shè)置參數(shù),例如: <interceptor-ref name="test"> <param name="expectedFoo">expectedFoo</param> </interceptor-ref> 注意:在Action引用的時(shí)候,如果引用的是Interceptor-stack,則不允許設(shè)置參數(shù),否則會(huì)報(bào)錯(cuò)。
Global-results:它允許我們定義全局的輸出結(jié)果(global result),比如登陸頁面、操作錯(cuò)誤處理頁面。只要繼承它所在的package,這些輸出結(jié)果都是可見的。 例如: <global-results> <result name="login" type="dispatcher"> <param name="location">/login.jsp</param> </result> <result name="error" type="dispatcher"> <param name="location">/error.jsp</param> </result> </global-results> 如果我們的Action執(zhí)行完返回“login”,它將調(diào)用上面的這個(gè)輸出結(jié)果,將輸出派遣到根目錄下的login.jsp頁面。
Action:用來配置Action的名稱(name)和它對(duì)應(yīng)的Class。我們將通過這個(gè)Action的名稱和它所在package的namespace去配置文件中取得這個(gè)Action的配置信息。它可以通過<param>來設(shè)置參數(shù),Action在執(zhí)行的時(shí)候會(huì)取得配置文件里設(shè)置的參數(shù)(通過攔截器StaticParametersInterceptor)。 Action可以配置一個(gè)或多個(gè)輸出結(jié)果(result)。一個(gè)輸出結(jié)果的名稱,對(duì)應(yīng)于Action執(zhí)行完成返回的字符串。<result>標(biāo)簽的type屬性,對(duì)應(yīng)我們前面定義過的result-type,說明reslut的類型。例如: <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> 當(dāng)然,我們的Action用到的攔截器也是在這里配置的,通過<interceptor-ref>標(biāo)簽,屬性“name”的值,對(duì)應(yīng)前面定義的interceptor或interceptor-stack。如果Action中沒有用<interceptor-ref>標(biāo)簽指定攔截器,它將使用默認(rèn)的<default-interceptor-ref>標(biāo)簽定義的攔截器。
Include:xwork..xml文件可以被分成好幾個(gè)不同的文件,xwork..xml通過<include>標(biāo)簽引用被包含的文件,例如:<include file="webwork-default.xml"/>。被包含的文件必需是package標(biāo)簽里的內(nèi)容,我們看看<include>標(biāo)簽在配置文件里的位置就知道了。如果要繼承被包含文件的package,我們必需將<include>標(biāo)簽放在其上面,因?yàn)榕渲梦募前凑沼缮隙碌捻樞蚪馕龅摹?/span>
實(shí)戰(zhàn)G-Roller-WWG-Roller-WW介紹JSTL與WebWork的整合中文解決方案
WebWork與其它開源項(xiàng)目的集成SpringHibernateXml-RPC
總結(jié)WebWork功能非常強(qiáng)大,除了上面介紹的以外,它還有很好的國際化支持功能,IoC(Inversion of control,依賴倒裝控制)框架支持;同時(shí),它也可以很好的與其它的開源項(xiàng)目集成,如:Sitemesh、Spring、Pico、Hibernate、JUnit、Quartz等。 “最好的文檔就是代碼”,WebWork代碼可讀性非常好,特別是2.1版本加了很多詳盡的注釋,在此向讀者強(qiáng)烈推薦,如果想更深入了解WebWork,建議多看它的代碼文檔。 到此,您已經(jīng)了解了WebWork的所有特性。它確實(shí)是一個(gè)非常優(yōu)秀的開源J2EE Web框架,同時(shí)我并不否定其它的框架,比如Struts,Tapestry,Maverick等,既然存在,它就一定有著自身存在價(jià)值和理由。 這么多的Web框架,有很多朋友在面臨選擇的時(shí)候也許會(huì)非常矛盾,不知應(yīng)該如何抉擇。在這,我的建議:關(guān)于開源Web框架的選擇,應(yīng)該根據(jù)團(tuán)隊(duì)的整體技術(shù)能力和要實(shí)施的項(xiàng)目來共同決定。關(guān)于是否要在項(xiàng)目中使用WebWork,假如你們已經(jīng)在團(tuán)隊(duì)中使用類似Struts這樣的J2EE框架,開發(fā)人員都已熟悉并有了很多技術(shù)和項(xiàng)目經(jīng)驗(yàn)的積累,那我建議你們暫時(shí)不要去使用WebWork,但我強(qiáng)烈建議找一個(gè)有代表性的模塊,將他們嘗試用WebWork改寫,我想,下個(gè)項(xiàng)目,也許你們就會(huì)改變注意,考慮使用WebWork。但,如果你們正在為具體選擇哪種Web框架而發(fā)愁,我相信WebWork一定是您最佳的選擇。 WebWork功能很多,但我們要牢記它的宗旨:致力于組件化和代碼重用,同時(shí)簡單、靈活,更好的提高生產(chǎn)率。
附錄我鐘愛的Opensympnony我是從WebWork開始認(rèn)識(shí)Opensymphony(http://www.)的,它是一個(gè)很好提供開源項(xiàng)目的組織。同Jakarta相比,這里的組件(Component)更多的是精致小巧的設(shè)計(jì),它們尤以簡單易用和可插拔的靈活性見長。除了我們這里介紹的WebWork和Xwork,下面我們將簡單介紹其他的一些組件:
OSWorkFlow:工作流引擎。它的流程定義靈活清晰,工作流引擎支持多種持久方式(MemoryStore ,SerializableStore, JDBCStore, OfbizStore, and EJBStore,HibernateStore等),具有極強(qiáng)的可擴(kuò)展性。它提供了強(qiáng)大的腳本支持(BeanShell、BSF等),多樣化的function,function可以直接使用普通java類函數(shù)、Xwork的Action、JMS、EJB、腳本等。它還提供了一個(gè)基于JGraph的流程設(shè)計(jì)器。 最新版本OSWorkFlow2.7也添加了對(duì)Spring框架的支持,流程設(shè)計(jì)器也有了很好的改進(jìn)。
Quartz:它是一個(gè)實(shí)現(xiàn)任務(wù)定時(shí)調(diào)度的框架,原先是一個(gè)獨(dú)立的project,后來并入OpenSymphony。它是一個(gè)非常輕量級(jí)的,并具有高度的可升級(jí)性,提供了簡單易用的接口。它提供了強(qiáng)大的任務(wù)調(diào)度運(yùn)行方式,可以獨(dú)立運(yùn)行、可以作為EJB部署于容器中、本身支持cluster,等等。 最新的版本是1.4,在性能和功能上都又有了很好的提高。
SiteMesh:它主要用來對(duì)Web頁面的布局管理,并且致力為很多頁面組成的大型網(wǎng)站提供提供統(tǒng)一的風(fēng)格、導(dǎo)航和布局功能。 它通過filter截取request和response,并給原始的頁面加入一定的裝飾(Decorator)(可能為header,footer...),然后把結(jié)果返回給客戶端,并且被裝飾的原始頁面并不知道SiteMesh的裝飾,這也就達(dá)到了解耦的目的。
OSCache:J2EE Caching機(jī)制。它主要用于JSP Caching、Request Caching、General-Purpose Cache三個(gè)方面。在JSP Caching、Request Caching方面,OSCache能夠解決動(dòng)態(tài)網(wǎng)站的基本問題:緩存動(dòng)態(tài)內(nèi)容、緩存二進(jìn)制內(nèi)容、錯(cuò)誤包容。在General-Purpose Cache方面,在Java應(yīng)用中通過調(diào)用OSCache的API來緩存任意的Java對(duì)象,hibernate 2.0開始對(duì)其也有支持。 OSCache標(biāo)記庫是一種開創(chuàng)性的JSP定制標(biāo)記應(yīng)用,提供了在現(xiàn)有JSP頁面之內(nèi)實(shí)現(xiàn)快速內(nèi)存緩沖的功能。雖然已經(jīng)有一些供應(yīng)商在提供各種形式的緩存產(chǎn)品,但是,它們都屬于面向特定供應(yīng)商的產(chǎn)品。OSCache能夠在任何JSP 1.2兼容的服務(wù)器上運(yùn)行,它不僅能夠?yàn)樗杏脩艟彌_現(xiàn)有JSP代碼塊,而且能夠以用戶為單位進(jìn)行緩沖。OSCache還包含一些提高可伸縮性的高級(jí)特性,比如:緩沖到磁盤,可編程的緩沖刷新,異常控制,等等。
OSCore:是一組公共工具類,提供了豐富的常用方法的標(biāo)準(zhǔn)實(shí)現(xiàn),供其他OpenSymphony組件使用。
PropertySet:管理屬性(Property)的好工具,它提供一個(gè)抽象方法來向一個(gè)持久性存儲(chǔ)源中動(dòng)態(tài)保存和取回類型化的屬性數(shù)據(jù)。支持多種持久化方式,例如:XML, EJB, Ofbiz, JDBC, Castor JDO,Memory等,同時(shí)也提供了一個(gè)簡單的API來根據(jù)你的需要寫你自己定制的PropertySets。
Clickstream:它是一個(gè)JavaServlet過濾器,用來跟蹤用戶請(qǐng)求(比如:點(diǎn)擊)和請(qǐng)求隊(duì)列(比如:點(diǎn)擊流)以向網(wǎng)絡(luò)管理員顯示誰在她的網(wǎng)站上以及每個(gè)用戶正在訪問那個(gè)頁面。
從技術(shù)的角度Struts1.1與WebWork2的比較
WebWork的項(xiàng)目資源下面是我在研究WebWork時(shí),使用的資源和研究過的項(xiàng)目,希望能對(duì)你能有幫助。 1、 當(dāng)然就是WebWork的官方網(wǎng)站:http://www./webwork/ 里面有最新的WebWork文檔和它的一個(gè)wiki 2、 一本好書:Java Open Source Programming : with XDoclet, JUnit, WebWork, Hibernate,里面有很好的WebWork教程。它附帶的源代碼可以去http://www.amazon.com/下載,里面的petsoar是一個(gè)非常好的WebWork開源項(xiàng)目。 3、 Confluence(http://www./software/confluence)是專業(yè)的J2EE wiki,用于知識(shí)管理和項(xiàng)目組交流。它使用的架構(gòu)是webwork2+Spring+Hibernate。Confluence雖是商業(yè)軟件,不過對(duì)于 OpenSource的項(xiàng)目它全部免費(fèi)提供。它的架構(gòu)思想很值得我們?nèi)W(xué)習(xí)。 4、 OpenReports(http://www.)是一個(gè)開源的項(xiàng)目,基于Web的報(bào)表系統(tǒng)。它用到的技術(shù)有:WebWork 2.0、Velocity和 Hibernate。 參考資料
注意:<result name="success" type="dispatcher"> <param name="location">register-result.jsp</param> </result> /register-result.jsp
location可以是一個(gè)絕對(duì)的URL,如http://www./也可以使用相對(duì)的URL。如果location以“/”開頭,則容器認(rèn)為相對(duì)于當(dāng)前Web應(yīng)用的根,否則,容器將解析為相對(duì)于當(dāng)前請(qǐng)求的URL。這種重定向結(jié)果,將導(dǎo)致客戶端瀏覽器的請(qǐng)求URL跳轉(zhuǎn)。從瀏覽器中的地址欄中可以看到新的URL地址。
location可以是一個(gè)絕對(duì)的URL,如response.sendRedirect("http://java.")也可以使用相對(duì)的URL。 如果location以“/”開頭,則容器認(rèn)為相對(duì)于當(dāng)前Web應(yīng)用的根,否則,容器將解析為相對(duì)于當(dāng)前請(qǐng)求的URL。 這種重定向的方法,將導(dǎo)致客戶端瀏覽器的請(qǐng)求URL跳轉(zhuǎn)。從瀏覽器中的地址欄中可以看到新的URL地址, 作用類似于上面設(shè)置HTTP響應(yīng)頭信息的實(shí)現(xiàn)。
在客戶端瀏覽器地址欄中不會(huì)顯示出轉(zhuǎn)向后的地址
RequestDispatcher是一個(gè)Web資源的包裝器,可以用來把當(dāng)前request傳遞到該資源, 或者把新的資源包括到當(dāng)前響應(yīng)中。 forward()方法將當(dāng)前的request和response重定向到該RequestDispacher指定的資源。 include()方法將把Request Dispatcher資源的輸出包含到當(dāng)前輸出中。
RequestDispatcher.forward()方法和HttpServletResponse.sendRedirect()方法的區(qū)別是: 前者僅是容器中控制權(quán)的轉(zhuǎn)向,在客戶端瀏覽器地址欄中不會(huì)顯示出轉(zhuǎn)向后的地址; 后者則是完全的跳轉(zhuǎn),瀏覽器將會(huì)得到跳轉(zhuǎn)的地址,并重新發(fā)送請(qǐng)求鏈接。 這樣,從瀏覽器的地址欄中可以看到跳轉(zhuǎn)后的鏈接地址。
注意:所有代碼程序的重新檢查。 |
|
|