ServletDispatcher
是默認的處理
Web Http
請求的調度器,它是一個
JavaServlet
,是
WebWork
框架的控制器。所有對
Action
調用的請求都將通過這個
ServletDispatcher
調度。它將在
web.xml
里配置
ServletDispatcher
時指定,讓所有對
WebWork
的
Action(
默認的是
.action
的后綴
)
的請求都對應到該調度的
JavaServlet
中,具體配置在前面的
WebWork-helloWorld
中有介紹。
?
ServletDispatcher
接受客戶端的
HTTP
請求,將
JavaServlet
的很多相關對象進行包裝,再傳給我們的
XWork
框架,由我們的
XWork
框架去解析我們的
xwork.xml
配置文件,根據配置文件的信息,創建對應的
Action
,組裝并調用相應的攔截器,執行
Action
,返回執行結果。
WebWork
使用
XWork
的核心,主要是由這個
ServletDispatcher
去實現的
?
具體調用流程:
(Servlet
調用流程
)
?
一、?
init
()方法在
web
服務器啟動時調用。
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
進行包裝的:
Request
包裝
protected Map getParameterMap(HttpServletRequest request) throws IOException {
??????? return request.getParameterMap();
}
這個方法比較簡單,它直接調用了
HttpServletRequest
的方法
getParameterMap
(),將所有
request
請求的參數封裝到一個
Map
中
?
Session
包裝
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
,它的構造函數操作有
:
?
???????????????????????? i.?????????????
由
com.opensymphony.xwork.ObjectFactory
創建我們配置文件描述的
Action
對象。再將這個
Action
對象存放入
OgnlValueStack
中。記得我們前面用戶注冊的例子嗎?當用戶提交表達時它會有表達式語言向
OgnlValueStack
取得
Action
對象的字段,再把輸入框的數據設置到對應的
Action
字段中,這個
Action
對象就是在這個時候進棧的
??????????????????????? ii.?????????????
傳入
extraContext
參數,創建與
ActionInvocation
對應的
Action
上下文(
ActionContext
)。記得我們在介紹
ActionContext
的最后,提出了一個需要注意的地方:不要在
Action
構造函數中調用
ActionContext.getContext()
?,F在應該能明白,原來是
Action
對象實例在
ActionContext
對象實例之前創建的,所有這樣取得
ActionContext
容器對象就有可能會返回
null
????????????????????? iii.?????????????
取得這個
Action
對應的所有攔截器(
Interceptor
),存放入
java.util.Iterator
對象中。
?
5、???????????
執行
proxy
的
execute()
方法,這個方法最核心的語句是:
retCode = invocation.invoke();
,
invocation
對象的
invoke()
方法它遍歷并執行這個
Action
對應的所有攔截器,執行
Action
對應的方法(默認的是
execute()
),根據
Action
執行返回的值去調用執行相應的
Result
(返回結果處理)的方法
?
理解了
ServletDispatcher
,我們就明白了整個框架調用執行的順序。
Action
雖然是與
Web
無關,可是它的創建、參數設置、執行與我們的
WebWork
、
XWork
緊密關聯在一起,有我們的控制器
ServletDispatcher
去統一調度,那我們如何去對
Action
進行獨立的單元測試呢?
請看下面的例子:使用單元測試框架
JUnit
對
register.User. RegisterAction
做單元測試
見
example.register. RegisterActionTest
類
testExecuteWithProxyFactory()
方法
?
public
void testExecuteWithProxyFactory() throws Exception{
???????
??????? //
創建
action
上下文(
actionContext
)
??????? 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);
???????
??????? //
創建代理(找出所在的
action
)
??????? ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("example", "register", extraContext);
?
??????? //
不要執行出現
Success
后的代碼
proxy.setExecuteResult(false);
???????
??????? //
測試代碼是否正確
??? ??? assertEquals(proxy.execute(),"success");
???????
??????? //
返回
Action
,測試
action
里面的代碼是否正確
??????? RegisterAction action = (RegisterAction) proxy.getAction();
??????? assertEquals(action.getUser().getUsername(),"Moxie");
??????? assertEquals(action.getUser().getAge(),23);
??? }
?
下面解說這個方法:
1、??
對象
params
表示請求參數的
Map,
在它里面設置了注冊用戶的信息。
extraContext
當然就是我們
ActionContext
上下文的容器,它里面保存了放置請求參數的對象
params
2、??
創建我們的
ActionProxy
,它傳入的參數有:“
example
”-這個
Action
的命名空間,“
register
”-
Action
對應的名字,
extraContext
-存放
Actin
上下文里的對象,,執行并將它返回的值與“
success
”比較,測試
Action
是否能正確執行完成。注意:
proxy.setExecuteResult(false);
,因為我們是單元測試,所以
Action
執行完成就可以了,不用再去調用結果響應的操作,故將是否執行結果設置為“
false
”。
3、??
Action
正確執行完成之后,我們也可以測試現在
Action
的字段里的數據是否按照我們預期的要求正確設置。從
ActionProxy
對象里取得執行的
Action
,即
RegisterAction
對象,再取得它的
User
模型,將其數據與前面設置參數的數據進行比較,判斷它是否等于我們預期設置的數值。
?
Result Type
前面我們學習了
ServletDispatcher
,它是
WebWork
框架機制的核心。它和
Action
在我們
MVC
模式中,扮演著控制器的角色,
MVC
模式通過控制器實現了我們模型和視圖的分離。
WebWork
提供了多種活靈活視圖展現方式。
我們先看看前面用戶注冊例子的展現方式:我們使用的是
Jsp
和
WebWork
自帶的標簽庫,
Action
對應的視圖當然是在
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>
?
Result
是
Action
執行完返回的一個字符串常量,它表示
Action
執行完成的狀態,比如:執行成功、執行失敗等。在我們前面
Action
的介紹中,詳細介紹了它默認的標準
Result
,當然
Result
我們也可以自己定義,只要是一個字符串常量就可以了。
?
Result
的值在
xwork.xml
配置文件里就是
result
標簽里“
name
”的值,
name="success"
表示
Action
執行成功,返回“
success
”就對應此標簽的配置,進行視圖輸出:
“
type
”就是我們的
Result Type
,
Result Type
是一個類,它在
Action
執行完成并返回
Result
之后,決定采用哪一種視圖技術,將執行結果展現給用戶。我們輸出的類型是
type="dispatcher"
,它對應
com.opensymphony.webwork.dispatcher.ServletDispatcherResult
這個類,它將執行結果通過
javax.servlet.RequestDispatcher
的
forward()
或
include()
方法調度到
Jsp
頁面展現。
我們可以自己開發
Result Type
,實現我們需要的視圖展現方式。
Result Type
必需要實現
com.opensymphony.xwork..Result
接口。在
WebWork
中,它已經為我們提供了很多
Result Type
,實現了視圖部分對
JSP, Velocity, FreeMarker, JasperReports
,
XML
等的支持,具體如下表格
:
Result Type
|
Nname
|
Class
|
Dispatcher
|
dispatcher
|
com.opensymphony.webwork.dispatcher.ServletDispatcherResult
|
Redirect
|
Redirect
|
com.opensymphony.webwork.dispatcher.ServletRedirectResult
|
Action Chaining
|
Chain
|
com.opensymphony.xwork.ActionChainResult
|
Velocity
|
Velocity
|
com.opensymphony.webwork.dispatcher.VelocityResult
|
FreeMarker
|
freemarker
|
com.opensymphony.webwork.views.freemarker.FreemarkerResult
|
JasperReports
|
Jasper
|
com.opensymphony.webwork.views.jasperreports.JasperReportsResult
|
XML/XSL
|
Xslt
|
com.opensymphony.webwork.views.xslt.XSLTResult
|
HttpHeader
|
?
|
com.opensymphony.webwork.dispatcher.HttpHeaderResult
|
?
?
Dispatcher
:
通過
javax.servlet.RequestDispatcher
的
forward()
或
include()
方法調度到頁面展現,這樣的頁面一般是
Jsp
頁面。
?
參數
(Parameters)
|
是否必需
|
描
?
述
|
location
|
是
|
執行完成之后轉向的位置
|
parse
|
否
|
默認的是“
true
”,如果設置為“
false
”,
location
參數將不會被
OGNL
表達式語言解析
|
例子:
<result name="success" type="dispatcher">
?????????? <param name="location">register-result.jsp</param>
???????? </result>
也可以簡單寫成這樣:
?? <result name="success" type="dispatcher">register-result.jsp</result>
?
?
Action Chaining
:
一種特殊的視圖結果,將
Action
執行完之后鏈接到另一個
Action
中繼續執行。新的
Action
使用上一個
Action
的上下文(
ActionContext
)。
?
參數
(Parameters)
|
是否必需
|
描
?
述
|
actionName
|
是
|
將要被鏈接的
Action
名字
|
namespace
|
否
|
被鏈接的
Action
的命名空間(
namespace
),如果不設置,默認的即是當前的命名空間
|
例子:
<result name="success" type="chain">
??? <param name="actionName">bar</param>
??? <param name="namespace">/foo</param>
</result>
?
將要調用的
Action
如下:
<action name="bar" class="myPackage.barAction">
??? ...
</action>
?
Velocity
:
它類似
Jsp
的執行環境(使用
JavaServlet
容器),將
Velocity
模板轉化成數據流的形式,直接通過
JavaServlet
輸出。
?
參數
(Parameters)
|
是否必需
|
描
?
述
|
location
|
是
|
執行完成之后轉向的位置
(
一般是
.vm
頁面
)
|
parse
|
否
|
默認的是“
true
”,如果設置為“
false
”,
location
參數將不會被
OGNL
表達式語言解析
|
例子:
<result name="success" type="velocity">
??? <param name="location">foo.vm</param>
</result>
?
?
FreeMarker
:
FreeMarker
是一個純
Java
模板引擎;一個普通的基于模板生成文本的工具,它只能應用在
Web
應用環境中。
?
參數
(Parameters)
|
是否必需
|
描
?
述
|
location
|
是
|
執行完成之后轉向的位置
|
parse
|
否
|
默認的是“
true
”,如果設置為“
false
”,
location
參數將不會被
OGNL
表達式語言解析
|
contentType
|
否
|
如果不指定,默認的是
"text/html"
|
例子:
<result name="success" type="freemarker">foo.ftl</result>
?
?
JasperReports
:
將
Action
執行的結果通過
JasperReports
報表形式輸出,可以指定
JasperReports
支持的輸出格式(
PDF
、
HTML
、
XLS
、
CSV
、
XML
等),默認是通過
PDF
格式輸出。
參數
(Parameters)
|
是否必需
|
描
?
述
|
location
|
是
|
執行完成之后轉向的位置
|
parse
|
否
|
默認的是“
true
”,如果設置為“
false
”,
location
參數將不會被
OGNL
表達式語言解析
|
dataSource
|
是
|
它是
Action
的一個字段(通常是一個
List
),
OGNL
表達式被用來去
value stack
(
OgnlValueStack
)重新找回這個
dataSource
|
format
|
否
|
報表生成的數據格式,默認的是
pdf
|
例子:
<result name="success" type="jasper">
??? <param name="location">foo.jasper</param>
??? <param name="dataSource">mySource</param>
??? <param name="format">CSV</param>
</result>
?
或者默認的
pdf
格式
<result name="success" type="jasper">
??? <param name="location">foo.jasper</param>
??? <param name="dataSource">mySource</param>
</result>
????????
XML/XSL
:
將結果轉換為
xml
輸出
?
參數
(Parameters)
|
是否必需
|
描
?
述
|
location
|
是
|
執行完成之后轉向的位置
|
parse
|
否
|
默認的是“
true
”,如果設置為“
false
”,
location
參數將不會被
OGNL
表達式語言解析
|
例子:
<result name="success" type="xslt">foo.xslt</result>??