天天看点

设计自己的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