取這樣一個標題太大,吸引眼球嘛@_@。
??? 事實是最近讀《J2EE設(shè)計模式》講述表達層模式的那幾章,書中有一個前端控制器+command模式的workflow例子,就琢磨著可以很簡單地擴展成一個MVC框架。花了一個下午改寫了下,對書中所述的理解更為深入。我想這也許對于學習和理解設(shè)計模式,以及初次接觸struts等MVC框架的人可能有點幫助。因為整個模型類似于struts,我把它取名叫strutslet^_^
(一)完整的類圖如下:
????? 
1。前端控制器(FrontController):前端控制器提供了一個統(tǒng)一的位置來封裝公共請求處理,它的任務(wù)相當簡單,執(zhí)行公共的任務(wù),然后把請求轉(zhuǎn)交給相應(yīng)的控制器。在strutslet中,前端控制器主要作用也在于此,它初始化并解析配置文件,接受每個請求,并簡單地把請求委托給調(diào)度器(Dispatcher),由調(diào)度器執(zhí)行相應(yīng)的動作(Action)。調(diào)度器把action返回的url返回給FrontController, FrontController負責轉(zhuǎn)發(fā)。
2。Action接口:command模式很好的例子,它是一個命令接口,每一個實現(xiàn)了此接口的action都封裝了某一個請求:新增一條數(shù)據(jù)記錄并更新model,或者把某個文件寫入磁盤。命令解耦了發(fā)送者和接受者之間聯(lián)系。發(fā)送者調(diào)用一個操作,接受者接受請求執(zhí)行相應(yīng)的動作,因為使用Command模式解耦,發(fā)送者無需知道接受者任何接口。
3。Dispatcher:調(diào)度器,負責流程的轉(zhuǎn)發(fā),負責調(diào)用action去執(zhí)行業(yè)務(wù)邏輯。由調(diào)度器選擇頁面和action,它去除了應(yīng)用行為和前端控制器間的耦合。調(diào)度器服務(wù)于前端控制器,它把model的更新委托給action,又提供頁面選擇給FrontController
4。ActionForward:封裝了轉(zhuǎn)向操作所需要信息的一個模型,包括name和轉(zhuǎn)向url
5。ActionModel:解析配置文件后,將每一個Action封裝成一個ActionModel對象,所有ActionModel構(gòu)成一個map,并存儲在ServletContext中,供整個框架使用。
(二)源代碼分析:
?1。Action接口,只有一個execute方法,任何一個action都只要實現(xiàn)此接口,并實現(xiàn)相應(yīng)的業(yè)務(wù)邏輯,最后返回一個ActionForward,提供給Dispacher調(diào)用。
?
package
?com.strutslet.core;

import
?javax.servlet.ServletContext;
import
?javax.servlet.http.HttpServletRequest;

import
?com.strutslet.model.ActionForward;


/**?*/
/**
?*?command接口
?*?
@author
?dennis
?*
?
*/
public
?
interface
?Action?
{
?
public
?ActionForward?execute(HttpServletRequest?request,ServletContext?context);?
}
?
比如,我們要實現(xiàn)一個登陸系統(tǒng),LoginAction驗證用戶名和密碼,如果正確,返回success頁面,如果登陸失敗,返回fail頁面:
package
?com.strutslet.demo;

import
?javax.servlet.ServletContext;
import
?javax.servlet.http.HttpServletRequest;

import
?com.strutslet.core.Action;
import
?com.strutslet.model.ActionForward;


public
?
class
?LoginAction?
implements
?Action?
{

?
private
?String?name
=
""
;
?
public
?ActionForward?execute(HttpServletRequest?request,

???ServletContext?context)?
{
??String?userName
=
request.getParameter(
"
userName
"
);
??String?password
=
request.getParameter(
"
password
"
);

????????
if
(userName.equals(
"
dennis
"
)
&&
password.equals(
"
123
"
))
{
??????request.setAttribute(
"
name
"
,?name);
??????
return
?ActionForward.SUCCESS;??
//
登陸成功,返回success
????????}
else
?????????
return
?ActionForward.FAIL;????
//
否則,返回fail
?}
}
2。還是先來看下兩個模型:ActionForward和ActionModel,沒什么東西,屬性以及相應(yīng)的getter,setter方法:
package
?com.strutslet.model;


/**?*/
/**
?*?類說明:轉(zhuǎn)向模型
?*?
@author
?dennis
?*
?*?
*/
public
?
class
?ActionForward?
{
?
private
?String?name;??????
//
forward的name
?
private
?String?viewUrl;???
//
forward的url
?
public
?
static
?
final
?ActionForward?SUCCESS
=
new
?ActionForward(
"
success
"
);
?
public
?
static
?
final
?ActionForward?FAIL
=
new
?ActionForward(
"
fail
"
);
?

?
public
??ActionForward(String?name)
{
??
this
.name
=
name;
?}
?
public
?ActionForward(String?name,?String?viewUrl)?
{
??
super
();
??
this
.name?
=
?name;
??
this
.viewUrl?
=
?viewUrl;
?}
?
//
name和viewUrl的getter和setter方法
}
???

我們看到ActionForward預(yù)先封裝了SUCCESS和FAIL對象。

//
ActionModel.java
package
?com.strutslet.model;

import
?java.util.Map;


/**?*/
/**
?*?類說明:
?*?
@author
?dennis
?*
?
*/
public
?
class
?ActionModel?
{
?
private
?String?path;?
//
?action的path
?
private
?String?className;?
//
?action的class
?
private
?Map
<
String,?ActionForward
>
?forwards;?
//
?action的forward
?
public
?ActionModel()
{}
?
?
public
?ActionModel(String?path,?String?className,

???Map
<
String,?ActionForward
>
?forwards)?
{
??
super
();
??
this
.path?
=
?path;
??
this
.className?
=
?className;
??
this
.forwards?
=
?forwards;
?}
?
//
相應(yīng)的getter和setter方法?????
}
3。知道了兩個模型是什么樣,也應(yīng)該可以猜到我們的配置文件大概是什么樣的了,與struts的配置文件格式類似:
?
<?
xml?version
=
"
1.0
"
?encoding
=
"
UTF-8
"
?>
<
actions
>
??
<
action?path
=
"
/login
"
??????????
class
=
"
com.strutslet.demo.LoginAction
"
>
?????
<
forward?name
=
"
success
"
?url
=
"
hello.jsp
"
/>
?????
<
forward?name
=
"
fail
"
?url
=
"
fail.jsp
"
/>
???
</
action
>
???????
</
actions
>
?
path是在應(yīng)用中將被調(diào)用的路徑,class指定了調(diào)用的哪個action,forward元素指定了轉(zhuǎn)向,比如我們這里如果是success就轉(zhuǎn)向hello.jsp,失敗的話轉(zhuǎn)向fail.jsp,這里配置了demo用到的LoginAction。
4。Dispacher接口,主要是getNextPage方法,此方法負責獲得下一個頁面將導向哪里,提供給前端控制器轉(zhuǎn)發(fā)。
package
?com.strutslet.core;


import
?javax.servlet.ServletContext;
import
?javax.servlet.http.HttpServletRequest;


/**?*/
/**
?*?service?to?worker模式,提供給FrontController使用
?*?負責流程轉(zhuǎn)發(fā)
?*?
@author
?dennis
?*
?
*/
public
?
interface
?Dispatcher?
{
?
public
?
void
?setServletContext(ServletContext?context);
?
public
?String?getNextPage(HttpServletRequest?request,ServletContext?context);
}
5。原先書中實現(xiàn)了一個WorkFlow的Dispatcher,按照順序調(diào)用action,實現(xiàn)工作流調(diào)用。而我們所需要的是根據(jù)請求的path 調(diào)用相應(yīng)的action,執(zhí)行action的execute方法返回一個ActionForward,然后得到ActionForward的 viewUrl,將此viewUrl提供給前端控制器轉(zhuǎn)發(fā),看看它的getNextPage方法:
public
?String?getNextPage(HttpServletRequest?request,?ServletContext?context)?
{
??setServletContext(context);

??Map
<
String,?ActionModel
>
?actions?
=
?(Map
<
String,?ActionModel
>
)?context
????.getAttribute(Constant.ACTIONS_ATTR);???
//
從ServletContext得到所有action信息
??String?reqPath?
=
?(String)?request.getAttribute(Constant.REQUEST_ATTR);
//
發(fā)起請求的path
??ActionModel?actionModel?
=
?actions.get(reqPath);??
//
根據(jù)path得到相應(yīng)的action
??String?forward_name?
=
?
""
;
??ActionForward?actionForward;

??
try
?
{
???Class?c?
=
?Class.forName(actionModel.getClassName());??
//
每個請求對應(yīng)一個action實例
???Action?action?
=
?(Action)?c.newInstance();
???actionForward?
=
?action.execute(request,?context);??
//
執(zhí)行action的execute方法
???forward_name?
=
?actionForward.getName();
???

??}
?
catch
?(Exception?e)?
{
???log.error(
"
can?not?find?action?
"
+
actionModel.getClassName());
???e.printStackTrace();
??}
??actionForward?
=
?actionModel.getForwards().get(forward_name);

??
if
?(actionForward?
==
?
null
)?
{
???log.error(
"
can?not?find?page?for?forward?
"
+
forward_name);
???
return
?
null
;
??}
?
else
???
return
?actionForward.getViewUrl();??????
//
返回ActionForward的viewUrl
?}
6。前端控制器(FrontController),它的任務(wù)我們已經(jīng)很清楚,初始化配置文件;存儲所有action到 ServletContext供整個框架使用;得到發(fā)起請求的path,提供給Dispachter查找相應(yīng)的action;調(diào)用Dispatcher,執(zhí)行g(shù)etNextPage方法得到下一個頁面的url并轉(zhuǎn)發(fā):
public
?
void
?init()?
throws
?ServletException?
{

??
//
初始化配置文件
??ServletContext?context
=
getServletContext();
??String?config_file?
=
getServletConfig().getInitParameter(
"
config
"
);
??String?dispatcher_name
=
getServletConfig().getInitParameter(
"
dispatcher
"
);
??
if
?(config_file?
==
?
null
?
||
?config_file.equals(
""
))
???config_file?
=
?
"
/WEB-INF/strutslet-config.xml
"
;?
//
默認是/WEB-INF/下面的strutslet-config
??
if
(dispatcher_name
==
null
||
dispatcher_name.equals(
""
))
???dispatcher_name
=
Constant.DEFAULT_DISPATCHER;
????

??
try
?
{
???Map
<
String,?ActionModel
>
?resources?
=
?ConfigUtil.newInstance()??
//
工具類解析配置文件
?????.parse(config_file,?context);
???context.setAttribute(Constant.ACTIONS_ATTR,?resources);??
//
存儲在ServletContext中
???log.info(
"
初始化strutslet配置文件成功
"
);

??}
?
catch
?(Exception?e)?
{
???log.error(
"
初始化strutslet配置文件失敗
"
);
???e.printStackTrace();
??}
??
//
實例化Dispacher
??
try
{
???Class?c?
=
?Class.forName(dispatcher_name);
??????Dispatcher?dispatcher?
=
?(Dispatcher)?c.newInstance();
??????context.setAttribute(Constant.DISPATCHER_ATTR,?dispatcher);?
//
放在ServletContext
??????log.info(
"
初始化Dispatcher成功
"
);

??}
catch
(Exception?e)?
{
????log.error(
"
初始化Dispatcher失敗
"
);
??????e.printStackTrace();
??}
??
..

doGet()和doPost方法我們都讓它調(diào)用process方法:
protected
?
void
?process(HttpServletRequest?request,

???HttpServletResponse?response)?
throws
?ServletException,?IOException?
{
??ServletContext?context?
=
?getServletContext();

????????
//
獲取action的path?
??String?reqURI?
=
?request.getRequestURI();
??
int
?i
=
reqURI.lastIndexOf(
"
.
"
);
??String?contextPath
=
request.getContextPath();
??String?path
=
reqURI.substring(contextPath.length(),i);
??
??request.setAttribute(Constant.REQUEST_ATTR,?path);
??Dispatcher?dispatcher?
=
?(Dispatcher)?context.getAttribute(Constant.DISPATCHER_ATTR);

??
//
?make?sure?we?don't?cache?dynamic?data
??response.setHeader(
"
Cache-Control
"
,?
"
no-cache
"
);
??response.setHeader(
"
Pragma
"
,?
"
no-cache
"
);

??
//
?use?the?dispatcher?to?find?the?next?page
??String?nextPage?
=
?dispatcher.getNextPage(request,?context);
//
調(diào)用Dispatcher的getNextPage

??
//
?forward?control?to?the?view
??RequestDispatcher?forwarder?
=
?request.getRequestDispatcher(
"
/
"
????
+
?nextPage);
??forwarder.forward(request,?response);??
//
轉(zhuǎn)發(fā)頁面
?}
7。最后,web.xml的配置就非常簡單了,配置前端控制器,提供啟動參數(shù)(配置文件所在位置,為空就查找/WEB-INF/下面的strutslet-config.xml文件),我們把所有以action結(jié)尾的請求都交給FrontController處理:
?
<
servlet
>
????
<
servlet
-
name
>
StrutsletController
</
servlet
-
name
>
????
<
servlet
-
class
>
com.strutslet.core.FrontController
</
servlet
-
class
>
????
<!--
??
????
<
init
-
param
>
?????????
<
param
-
name
>
config
</
param
-
name
>
?????????
<
param
-
value
>/
WEB
-
INF
/
strutslet
-
config.xml
</
param
-
value
>
????
</
init
-
param
>
????
-->
???????
<
load
-
on
-
startup
>
0
</
load
-
on
-
startup
>
??
</
servlet
>
?
<
servlet
-
mapping
>
????
<
servlet
-
name
>
StrutsletController
</
servlet
-
name
>
????
<
url
-
pattern
>*
.action
</
url
-
pattern
>
?
</
servlet
-
mapping
>
?
最后,讓我們看看整個框架圖:
?