莊周夢蝶

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

          設計自己的MVC框架

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

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

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

          (一)完整的類圖如下:

          ????? 10fd68bb171.jpg

          1。前端控制器(FrontController):前端控制器提供了一個統一的位置來封裝公共請求處理,它的任務相當簡單,執行公共的任務,然后把請求轉交給相應的控制器。在strutslet中,前端控制器主要作用也在于此,它初始化并解析配置文件,接受每個請求,并簡單地把請求委托給調度器(Dispatcher),由調度器執行相應的動作(Action)。調度器把action返回的url返回給FrontController, FrontController負責轉發。

          2。Action接口:command模式很好的例子,它是一個命令接口,每一個實現了此接口的action都封裝了某一個請求:新增一條數據記錄并更新model,或者把某個文件寫入磁盤。命令解耦了發送者和接受者之間聯系。發送者調用一個操作,接受者接受請求執行相應的動作,因為使用Command模式解耦,發送者無需知道接受者任何接口。

          3。Dispatcher:調度器,負責流程的轉發,負責調用action去執行業務邏輯。由調度器選擇頁面和action,它去除了應用行為和前端控制器間的耦合。調度器服務于前端控制器,它把model的更新委托給action,又提供頁面選擇給FrontController

          4。ActionForward:封裝了轉向操作所需要信息的一個模型,包括name和轉向url

          5。ActionModel:解析配置文件后,將每一個Action封裝成一個ActionModel對象,所有ActionModel構成一個map,并存儲在ServletContext中,供整個框架使用。

          (二)源代碼分析:

          ?1。Action接口,只有一個execute方法,任何一個action都只要實現此接口,并實現相應的業務邏輯,最后返回一個ActionForward,提供給Dispacher調用。

          ?

          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);?
          }


          ?

          比如,我們要實現一個登陸系統,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,沒什么東西,屬性以及相應的getter,setter方法:

          package ?com.strutslet.model;

          /**
          ?*?類說明:轉向模型
          ?*?
          @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預先封裝了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;
          ?}



          ?
          // 相應的getter和setter方法?????

          }



          3。知道了兩個模型是什么樣,也應該可以猜到我們的配置文件大概是什么樣的了,與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是在應用中將被調用的路徑,class指定了調用的哪個action,forward元素指定了轉向,比如我們這里如果是success就轉向hello.jsp,失敗的話轉向fail.jsp,這里配置了demo用到的LoginAction。

          4。Dispacher接口,主要是getNextPage方法,此方法負責獲得下一個頁面將導向哪里,提供給前端控制器轉發。

          package ?com.strutslet.core;


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

          /**
          ?*?service?to?worker模式,提供給FrontController使用
          ?*?負責流程轉發
          ?*?
          @author ?dennis
          ?*
          ?
          */

          public ? interface ?Dispatcher? {
          ?
          public ? void ?setServletContext(ServletContext?context);
          ?
          public ?String?getNextPage(HttpServletRequest?request,ServletContext?context);
          }



          5。原先書中實現了一個WorkFlow的Dispatcher,按照順序調用action,實現工作流調用。而我們所需要的是根據請求的path 調用相應的action,執行action的execute方法返回一個ActionForward,然后得到ActionForward的 viewUrl,將此viewUrl提供給前端控制器轉發,看看它的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); // 發起請求的path
          ??ActionModel?actionModel? = ?actions.get(reqPath);?? // 根據path得到相應的action
          ??String?forward_name? = ? "" ;
          ??ActionForward?actionForward;
          ??
          try ? {
          ???Class?c?
          = ?Class.forName(actionModel.getClassName());?? // 每個請求對應一個action實例

          ???Action?action?
          = ?(Action)?c.newInstance();
          ???actionForward?
          = ?action.execute(request,?context);?? // 執行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),它的任務我們已經很清楚,初始化配置文件;存儲所有action到 ServletContext供整個框架使用;得到發起請求的path,提供給Dispachter查找相應的action;調用Dispatcher,執行getNextPage方法得到下一個頁面的url并轉發:

          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方法我們都讓它調用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); // 調用Dispatcher的getNextPage

          ??
          // ?forward?control?to?the?view
          ??RequestDispatcher?forwarder? = ?request.getRequestDispatcher( " / "
          ????
          + ?nextPage);
          ??forwarder.forward(request,?response);??
          // 轉發頁面
          ?}



          7。最后,web.xml的配置就非常簡單了,配置前端控制器,提供啟動參數(配置文件所在位置,為空就查找/WEB-INF/下面的strutslet-config.xml文件),我們把所有以action結尾的請求都交給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: 設計自己的MVC框架  回復  更多評論   

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

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

          2012-02-22 10:26 by mu00000
          不知道源碼還有不。想學習下,樓主能幫忙嗎。312558613@qq.com,萬分感激。
          主站蜘蛛池模板: 修文县| 兴和县| 沙坪坝区| 志丹县| 石狮市| 寻乌县| 那曲县| 邛崃市| 紫阳县| 吉木萨尔县| 绍兴县| 巴林左旗| 虞城县| 康保县| 宽甸| 湘潭市| 昆明市| 翼城县| 海南省| 五大连池市| 青铜峡市| 西林县| 高雄市| 安远县| 全州县| 哈密市| 普兰店市| 湘潭市| 晴隆县| 交口县| 苍南县| 高淳县| 西丰县| 瑞昌市| 佳木斯市| 万荣县| 萝北县| 遂川县| 兰溪市| 台中市| 湟中县|