天天看點

設計自己的MVC架構

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

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

(一)完整的類圖如下:

設計自己的MVC架構

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調用。

設計自己的MVC架構

 package  com.strutslet.core;

設計自己的MVC架構
設計自己的MVC架構

 import  javax.servlet.servletcontext;

設計自己的MVC架構

 import  javax.servlet.http.httpservletrequest;

設計自己的MVC架構
設計自己的MVC架構

 import  com.strutslet.model.actionforward;

設計自己的MVC架構
設計自己的MVC架構

 /** 

設計自己的MVC架構

 * command接口

設計自己的MVC架構

 *  @author  dennis

設計自己的MVC架構

 *

設計自己的MVC架構

  */ 

設計自己的MVC架構

 public   interface  action  {

設計自己的MVC架構

  public  actionforward execute(httpservletrequest request,servletcontext context); 

設計自己的MVC架構

設計自己的MVC架構
設計自己的MVC架構

比如,我們要實作一個登陸系統,loginaction驗證使用者名和密碼,如果正确,傳回success頁面,如果登陸失敗,傳回fail頁面:

設計自己的MVC架構

 package  com.strutslet.demo;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 import  com.strutslet.core.action;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 public   class  loginaction  implements  action  {

設計自己的MVC架構
設計自己的MVC架構

  private  string name = "" ;

設計自己的MVC架構

  public  actionforward execute(httpservletrequest request,

設計自己的MVC架構

   servletcontext context)  {

設計自己的MVC架構

  string username = request.getparameter( " username " );

設計自己的MVC架構

  string password = request.getparameter( " password " );

設計自己的MVC架構

         if (username.equals( " dennis " ) && password.equals( " 123 " )) {

設計自己的MVC架構

      request.setattribute( " name " , name);

設計自己的MVC架構

       return  actionforward.success;   // 登陸成功,傳回success 

設計自己的MVC架構

         } else 

設計自己的MVC架構

          return  actionforward.fail;     // 否則,傳回fail 

設計自己的MVC架構

  } 

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

2。還是先來看下兩個模型:actionforward和actionmodel,沒什麼東西,屬性以及相應的getter,setter方法:

設計自己的MVC架構

 package  com.strutslet.model;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 * 類說明:轉向模型

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 *  */ 

設計自己的MVC架構

 public   class  actionforward  {

設計自己的MVC架構

  private  string name;       // forward的name 

設計自己的MVC架構

   private  string viewurl;    // forward的url 

設計自己的MVC架構

   public   static   final  actionforward success = new  actionforward( " success " );

設計自己的MVC架構

  public   static   final  actionforward fail = new  actionforward( " fail " );

設計自己的MVC架構
設計自己的MVC架構

  public   actionforward(string name) {

設計自己的MVC架構

   this .name = name;

設計自己的MVC架構

 } 

設計自己的MVC架構
設計自己的MVC架構

   public  actionforward(string name, string viewurl)  {

設計自己的MVC架構

   super ();

設計自己的MVC架構

   this .name  =  name;

設計自己的MVC架構

   this .viewurl  =  viewurl;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

  // 

設計自己的MVC架構

name和viewurl的getter和setter方法 

設計自己的MVC架構
設計自己的MVC架構

}    

設計自己的MVC架構
設計自己的MVC架構

我們看到actionforward預先封裝了success和fail對象。

設計自己的MVC架構
設計自己的MVC架構

 // actionmodel.java 

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 import  java.util.map;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 * 類說明:

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 public   class  actionmodel  {

設計自己的MVC架構

  private  string path;  //  action的path 

設計自己的MVC架構
設計自己的MVC架構

  private  string classname;  //  action的class 

設計自己的MVC架構
設計自己的MVC架構

  private  map < string, actionforward >  forwards;  //  action的forward 

設計自己的MVC架構
設計自己的MVC架構

   public  actionmodel() {} 

設計自己的MVC架構
設計自己的MVC架構

  public  actionmodel(string path, string classname,

設計自己的MVC架構

   map < string, actionforward >  forwards)  {

設計自己的MVC架構
設計自己的MVC架構

   this .path  =  path;

設計自己的MVC架構

   this .classname  =  classname;

設計自己的MVC架構

   this .forwards  =  forwards;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

相應的getter和setter方法      

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

3。知道了兩個模型是什麼樣,也應該可以猜到我們的配置檔案大概是什麼樣的了,與struts的配置檔案格式類似:

設計自己的MVC架構

 <? xml version = " 1.0 "  encoding = " utf-8 " ?> 

設計自己的MVC架構

 < actions > 

設計自己的MVC架構

   < action path = " /login " 

設計自己的MVC架構

           class = " com.strutslet.demo.loginaction " > 

設計自己的MVC架構

      < forward name = " success "  url = " hello.jsp " /> 

設計自己的MVC架構

      < forward name = " fail "  url = " fail.jsp " /> 

設計自己的MVC架構

    </ action >        

設計自己的MVC架構

 </ actions >

path是在應用中将被調用的路徑,class指定了調用的哪個action,forward元素指定了轉向,比如我們這裡如果是success就轉向hello.jsp,失敗的話轉向fail.jsp,這裡配置了demo用到的loginaction。

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

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 * service to worker模式,提供給frontcontroller使用

設計自己的MVC架構

 * 負責流程轉發

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

 public   interface  dispatcher  {

設計自己的MVC架構

  public   void  setservletcontext(servletcontext context);

設計自己的MVC架構

  public  string getnextpage(httpservletrequest request,servletcontext context);

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

5。原先書中實作了一個workflow的dispatcher,按照順序調用action,實作工作流調用。而我們所需要的是根據請求的path 調用相應的action,執行action的execute方法傳回一個actionforward,然後得到actionforward的 viewurl,将此viewurl提供給前端控制器轉發,看看它的getnextpage方法:

設計自己的MVC架構

 public  string getnextpage(httpservletrequest request, servletcontext context)  {

設計自己的MVC架構

  setservletcontext(context);

設計自己的MVC架構
設計自己的MVC架構

  map < string, actionmodel >  actions  =  (map < string, actionmodel > ) context

設計自己的MVC架構

    .getattribute(constant.actions_attr);    // 從servletcontext得到所有action資訊 

設計自己的MVC架構

   string reqpath  =  (string) request.getattribute(constant.request_attr); // 發起請求的path 

設計自己的MVC架構

   actionmodel actionmodel  =  actions.get(reqpath);   // 根據path得到相應的action 

設計自己的MVC架構

   string forward_name  =   "" ;

設計自己的MVC架構

  actionforward actionforward;

設計自己的MVC架構

   try   {

設計自己的MVC架構

   class c  =  class.forname(actionmodel.getclassname());   // 每個請求對應一個action執行個體 

設計自己的MVC架構
設計自己的MVC架構

   action action  =  (action) c.newinstance();

設計自己的MVC架構

   actionforward  =  action.execute(request, context);   // 執行action的execute方法 

設計自己的MVC架構

    forward_name  =  actionforward.getname();

設計自己的MVC架構
設計自己的MVC架構

  }   catch  (exception e)  {

設計自己的MVC架構

   log.error( " can not find action  " + actionmodel.getclassname());

設計自己的MVC架構

   e.printstacktrace();

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

  actionforward  =  actionmodel.getforwards().get(forward_name);

設計自己的MVC架構

   if  (actionforward  ==   null )  {

設計自己的MVC架構

   log.error( " can not find page for forward  " + forward_name);

設計自己的MVC架構

    return   null ;

設計自己的MVC架構

  }   else 

設計自己的MVC架構

    return  actionforward.getviewurl();       // 傳回actionforward的viewurl 

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

6。前端控制器(frontcontroller),它的任務我們已經很清楚,初始化配置檔案;存儲所有action到 servletcontext供整個架構使用;得到發起請求的path,提供給dispachter查找相應的action;調用dispatcher,執行getnextpage方法得到下一個頁面的url并轉發:

設計自己的MVC架構

 public   void  init()  throws  servletexception  {

設計自己的MVC架構
設計自己的MVC架構

   // 初始化配置檔案 

設計自己的MVC架構
設計自己的MVC架構

  servletcontext context = getservletcontext();

設計自己的MVC架構

  string config_file  = getservletconfig().getinitparameter( " config " );

設計自己的MVC架構

  string dispatcher_name = getservletconfig().getinitparameter( " dispatcher " );

設計自己的MVC架構

   if  (config_file  ==   null   ||  config_file.equals( "" ))

設計自己的MVC架構

   config_file  =   " /web-inf/strutslet-config.xml " ;  // 預設是/web-inf/下面的strutslet-config 

設計自己的MVC架構

    if (dispatcher_name == null || dispatcher_name.equals( "" ))

設計自己的MVC架構

   dispatcher_name = constant.default_dispatcher;

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

   map < string, actionmodel >  resources  =  configutil.newinstance()   // 工具類解析配置檔案 

設計自己的MVC架構

      .parse(config_file, context);

設計自己的MVC架構

   context.setattribute(constant.actions_attr, resources);   // 存儲在servletcontext中 

設計自己的MVC架構

    log.info( " 初始化strutslet配置檔案成功 " );

設計自己的MVC架構
設計自己的MVC架構

   log.error( " 初始化strutslet配置檔案失敗 " );

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

   // 執行個體化dispacher 

設計自己的MVC架構
設計自己的MVC架構

    try {

設計自己的MVC架構

   class c  =  class.forname(dispatcher_name);

設計自己的MVC架構

      dispatcher dispatcher  =  (dispatcher) c.newinstance();

設計自己的MVC架構

      context.setattribute(constant.dispatcher_attr, dispatcher);  // 放在servletcontext 

設計自己的MVC架構

       log.info( " 初始化dispatcher成功 " );

設計自己的MVC架構

  } catch (exception e)  {

設計自己的MVC架構

    log.error( " 初始化dispatcher失敗 " );

設計自己的MVC架構

      e.printstacktrace();

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

..

設計自己的MVC架構
設計自己的MVC架構

doget()和dopost方法我們都讓它調用process方法:

設計自己的MVC架構

 protected   void  process(httpservletrequest request,

設計自己的MVC架構

   httpservletresponse response)  throws  servletexception, ioexception  {

設計自己的MVC架構

  servletcontext context  =  getservletcontext();

設計自己的MVC架構
設計自己的MVC架構

         // 擷取action的path  

設計自己的MVC架構

   string requri  =  request.getrequesturi();

設計自己的MVC架構

   int  i = requri.lastindexof( " . " );

設計自己的MVC架構

  string contextpath = request.getcontextpath();

設計自己的MVC架構

  string path = requri.substring(contextpath.length(),i);

設計自己的MVC架構
設計自己的MVC架構

  request.setattribute(constant.request_attr, path);

設計自己的MVC架構

  dispatcher dispatcher  =  (dispatcher) context.getattribute(constant.dispatcher_attr);

設計自己的MVC架構
設計自己的MVC架構

   //  make sure we don't cache dynamic data 

設計自己的MVC架構

   response.setheader( " cache-control " ,  " no-cache " );

設計自己的MVC架構

  response.setheader( " pragma " ,  " no-cache " );

設計自己的MVC架構
設計自己的MVC架構

   //  use the dispatcher to find the next page 

設計自己的MVC架構

   string nextpage  =  dispatcher.getnextpage(request, context); // 調用dispatcher的getnextpage

設計自己的MVC架構
設計自己的MVC架構

   //  forward control to the view 

設計自己的MVC架構

   requestdispatcher forwarder  =  request.getrequestdispatcher( " / " 

設計自己的MVC架構

     +  nextpage);

設計自己的MVC架構

  forwarder.forward(request, response);   // 轉發頁面 

設計自己的MVC架構
設計自己的MVC架構
設計自己的MVC架構

7。最後,web.xml的配置就非常簡單了,配置前端控制器,提供啟動參數(配置檔案所在位置,為空就查找/web-inf/下面的strutslet-config.xml檔案),我們把所有以action結尾的請求都交給frontcontroller處理:

設計自己的MVC架構

 < servlet > 

設計自己的MVC架構

     < servlet - name > strutsletcontroller </ servlet - name > 

設計自己的MVC架構

     < servlet - class > com.strutslet.core.frontcontroller </ servlet - class > 

設計自己的MVC架構

     <!--   

設計自己的MVC架構

     < init - param > 

設計自己的MVC架構

          < param - name > config </ param - name > 

設計自己的MVC架構

          < param - value >/ web - inf / strutslet - config.xml </ param - value > 

設計自己的MVC架構

     </ init - param > 

設計自己的MVC架構

     --> 

設計自己的MVC架構

        < load - on - startup > 0 </ load - on - startup > 

設計自己的MVC架構

   </ servlet > 

設計自己的MVC架構

  < servlet - mapping > 

設計自己的MVC架構
設計自己的MVC架構

     < url - pattern >* .action </ url - pattern > 

設計自己的MVC架構

  </ servlet - mapping >

文章轉自莊周夢蝶  ,原文釋出5.16