<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    設(shè)計自己的MVC框架

    Posted on 2007-02-06 11:54 dennis 閱讀(2425) 評論(2)  編輯  收藏 所屬分類: 模式與架構(gòu)

    取這樣一個標題太大,吸引眼球嘛@_@。

    ??? 事實是最近讀《J2EE設(shè)計模式》講述表達層模式的那幾章,書中有一個前端控制器+command模式的workflow例子,就琢磨著可以很簡單地擴展成一個MVC框架。花了一個下午改寫了下,對書中所述的理解更為深入。我想這也許對于學習和理解設(shè)計模式,以及初次接觸struts等MVC框架的人可能有點幫助。因為整個模型類似于struts,我把它取名叫strutslet^_^

    (一)完整的類圖如下:

    ????? 10fd68bb171.jpg

    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 >

    ?

    最后,讓我們看看整個框架圖:

    ?test.jpg


    評論

    # re: 設(shè)計自己的MVC框架  回復  更多評論   

    2007-03-25 00:06 by Cherokee
    不錯最近正好在研究MVC

    # re: 設(shè)計自己的MVC框架  回復  更多評論   

    2012-02-22 10:26 by mu00000
    不知道源碼還有不。想學習下,樓主能幫忙嗎。312558613@qq.com,萬分感激。
    主站蜘蛛池模板: 久久亚洲最大成人网4438| 亚洲AV无码国产精品麻豆天美| 亚洲精品美女网站| 2021精品国产品免费观看| 亚洲av网址在线观看| 在线看片免费人成视频播| 亚洲成a人片在线观看无码| 一区二区三区无码视频免费福利| 亚洲日韩激情无码一区| 久久免费国产视频| 久久精品国产亚洲AV无码麻豆| 精品国产免费人成电影在线观看| 亚洲人成在线免费观看| 日韩精品成人无码专区免费| 国内精品久久久久影院亚洲| 日本不卡在线观看免费v| 思思久久99热免费精品6| 国产亚洲精午夜久久久久久| 无码日韩精品一区二区免费暖暖| 久久精品国产亚洲AV嫖农村妇女 | 日韩精品无码免费专区午夜不卡| 国产亚洲日韩一区二区三区| 国产无遮挡无码视频免费软件| 亚洲福利视频网址| 扒开双腿猛进入爽爽免费视频| 免费夜色污私人影院网站| 国产AV无码专区亚洲AV手机麻豆 | 亚洲精品国产第一综合99久久 | 久久国产乱子伦精品免费看| 亚洲成人网在线播放| 国产精品视_精品国产免费| 成人A片产无码免费视频在线观看| 久久久久亚洲精品日久生情| 国产男女性潮高清免费网站| 韩日电影在线播放免费版| 亚洲国产日韩在线人成下载 | 久久亚洲伊人中字综合精品| 久久久久国色AV免费观看性色| 黄色一级视频免费观看| 久久精品国产亚洲AV电影| 又粗又大又硬又爽的免费视频 |