zoukankan      html  css  js  c++  java
  • 设计模式:设计自己的MVC框架(转)

    源代码放在sharesources@126.com的邮箱的收件箱里,用户名:sharesource,密码:javafans
    希望保留给有用的人,谢谢。

        取这样一个标题太大,吸引眼球嘛@_@。

        事实是最近读《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调用。
    1. public interface Action {
    2.  public ActionForward execute(HttpServletRequest request,ServletContext context); 
    3. }


    比如,我们要实现一个登陆系统(demo的例子),LoginAction验证用户名和密码,如果正确,返回success页面,如果登陆失败,返回fail页面:
    1. public class LoginAction implements Action {
    2.  private String name="";
    3.  public ActionForward execute(HttpServletRequest request,
    4.    ServletContext context) {
    5.   String userName=request.getParameter("userName");
    6.   String password=request.getParameter("password");
    7.         if(userName.equals("dennis")&&password.equals("123")){
    8.       request.setAttribute("name", name);
    9.       return ActionForward.SUCCESS;  //登陆成功,返回success
    10.         }else
    11.          return ActionForward.FAIL;    //否则,返回fail
    12.  }


     

    2.还是先来看下两个模型:ActionForward和ActionModel,没什么东西,属性以及相应的getter,setter方法:

    1. /**
    2.  * 类说明:转向模型
    3.  * @author dennis
    4.  *
    5.  * */
    6. public class ActionForward {
    7.  private String name;      //forward的name
    8.  private String viewUrl;   //forward的url
    9.  public static final ActionForward SUCCESS=new ActionForward("success");
    10.  public static final ActionForward FAIL=new ActionForward("fail");
    11.  public  ActionForward(String name){
    12.   this.name=name;
    13.  }
    14.  public ActionForward(String name, String viewUrl) {
    15.   super();
    16.   this.name = name;
    17.   this.viewUrl = viewUrl;
    18.  }
    19.  //...name和viewUrl的getter和setter方法
    20. }   


    我们看到ActionForward预先封装了SUCCESS和FAIL对象。

    1. public class ActionModel {
    2.  private String path; // action的path
    3.  private String className; // action的class
    4.  private Map<String, ActionForward> forwards; // action的forward
    5.  public ActionModel(){}
    6.  public ActionModel(String path, String className,
    7.    Map<String, ActionForward> forwards) {
    8.   super();
    9.   this.path = path;
    10.   this.className = className;
    11.   this.forwards = forwards;
    12.  }
    13.  //...相应的getter和setter方法     
    14. }



    3。知道了两个模型是什么样,也应该可以猜到我们的配置文件大概是什么样的了,与struts的配置文件格式类似:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <actions>
    3.   <action path="/login"
    4.           class="com.strutslet.demo.LoginAction">
    5.      <forward name="success" url="hello.jsp"/>
    6.      <forward name="fail" url="fail.jsp"/>
    7.    </action>       
    8. </actions>


    path是在应用中将被调用的路径,class指定了调用的哪个action,forward元素指定了转向,比如我们这里如果是success就转向hello.jsp,失败的话转向fail.jsp,这里配置了demo用到的LoginAction。

    4。Dispacher接口,主要是getNextPage方法,此方法负责获得下一个页面将导向哪里,提供给前端控制器转发。

    1. public interface Dispatcher {
    2.  public void setServletContext(ServletContext context);
    3.  public String getNextPage(HttpServletRequest request,ServletContext context);
    4. }



    5。5。原先书中实现了一个WorkFlow的Dispatcher,按照顺序调用action,实现工作流调用。而我们所需要的是根据请求的path调用相应的action,执行action的execute方法返回一个ActionForward,然后得到ActionForward的viewUrl,将此viewUrl提供给前端控制器转发,看看它的getNextPage方法:

    1. public String getNextPage(HttpServletRequest request, ServletContext context) {
    2.   setServletContext(context);
    3.   Map<String, ActionModel> actions = (Map<String, ActionModel>) context
    4.     .getAttribute(Constant.ACTIONS_ATTR);   //从ServletContext得到所有action信息
    5.   String reqPath = (String) request.getAttribute(Constant.REQUEST_ATTR);//发起请求的path
    6.   ActionModel actionModel = actions.get(reqPath);  //根据path得到相应的action
    7.   String forward_name = "";
    8.   ActionForward actionForward;
    9.   try {
    10.    Class c = Class.forName(actionModel.getClassName());  //每个请求对应一个action实例
    11.    Action action = (Action) c.newInstance();
    12.    actionForward = action.execute(request, context);  //执行action的execute方法
    13.    forward_name = actionForward.getName();
    14.    
    15.   } catch (Exception e) {
    16.    log.error("can not find action "+actionModel.getClassName());
    17.    e.printStackTrace();
    18.   }
    19.   actionForward = actionModel.getForwards().get(forward_name);
    20.   if (actionForward == null) {
    21.    log.error("can not find page for forward "+forward_name);
    22.    return null;
    23.   } else
    24.    return actionForward.getViewUrl();      //返回ActionForward的viewUrl
    25.  }



     

    6。前端控制器(FrontController),它的任务我们已经很清楚,初始化配置文件;存储所有action到ServletContext供整个框架使用;得到发起请求的path,提供给Dispachter查找相应的action;调用Dispatcher,执行getNextPage方法得到下一个页面的url并转发:

    1. public void init() throws ServletException {
    2.   //初始化配置文件
    3.   ServletContext context=getServletContext();
    4.   String config_file =getServletConfig().getInitParameter("config");
    5.   String dispatcher_name=getServletConfig().getInitParameter("dispatcher");
    6.   if (config_file == null || config_file.equals(""))
    7.    config_file = "/WEB-INF/strutslet-config.xml"//默认是/WEB-INF/下面的strutslet-config
    8.   if(dispatcher_name==null||dispatcher_name.equals(""))
    9.    dispatcher_name=Constant.DEFAULT_DISPATCHER;
    10.     
    11.   try {
    12.    Map<String, ActionModel> resources = ConfigUtil.newInstance()  //工具类解析配置文件
    13.      .parse(config_file, context);
    14.    context.setAttribute(Constant.ACTIONS_ATTR, resources);  //存储在ServletContext中
    15.    log.info("初始化strutslet配置文件成功");
    16.   } catch (Exception e) {
    17.    log.error("初始化strutslet配置文件失败");
    18.    e.printStackTrace();
    19.   }
    20.   //实例化Dispacher
    21.   try{
    22.    Class c = Class.forName(dispatcher_name);
    23.       Dispatcher dispatcher = (Dispatcher) c.newInstance();
    24.       context.setAttribute(Constant.DISPATCHER_ATTR, dispatcher); //放在ServletContext
    25.       log.info("初始化Dispatcher成功");
    26.   }catch(Exception e) {
    27.     log.error("初始化Dispatcher失败");
    28.       e.printStackTrace();
    29.   }
    30.   .....



    doGet()和doPost方法我们都让它调用process方法:

    1. protected void process(HttpServletRequest request,
    2.    HttpServletResponse response) throws ServletException, IOException {
    3.   ServletContext context = getServletContext();
    4.         //获取action的path 
    5.   String reqURI = request.getRequestURI();
    6.   int i=reqURI.lastIndexOf(".");
    7.   String contextPath=request.getContextPath();
    8.   String path=reqURI.substring(contextPath.length(),i);
    9.   
    10.   request.setAttribute(Constant.REQUEST_ATTR, path);
    11.   Dispatcher dispatcher = (Dispatcher) context.getAttribute(Constant.DISPATCHER_ATTR);
    12.   // make sure we don't cache dynamic data
    13.   response.setHeader("Cache-Control""no-cache");
    14.   response.setHeader("Pragma""no-cache");
    15.   // use the dispatcher to find the next page
    16.   String nextPage = dispatcher.getNextPage(request, context);//调用Dispatcher的getNextPage
    17.   // forward control to the view
    18.   RequestDispatcher forwarder = request.getRequestDispatcher("/"
    19.     + nextPage);
    20.   forwarder.forward(request, response);  //转发页面
    21.  }



    7。最后,web.xml的配置就非常简单了,配置前端控制器,提供启动参数(配置文件所在位置,为空就查找/WEB-INF/下面的strutslet-config.xml文件),我们把所有以action结尾的请求都交给FrontController处理:

    1. <servlet>
    2.     <servlet-name>StrutsletController</servlet-name>
    3.     <servlet-class>com.strutslet.core.FrontController</servlet-class>
    4.     <!--  
    5.     <init-param>
    6.          <param-name>config</param-name>
    7.          <param-value>/WEB-INFstrutslet-config.xml</param-value>
    8.     </init-param>
    9.     -->
    10.        <load-on-startup>0</load-on-startup>
    11.   </servlet>
    12.  <servlet-mapping>
    13.     <servlet-name>StrutsletController</servlet-name>
    14.     <url-pattern>*.action</url-pattern>
    15.  </servlet-mapping>



    最后,让我们看看整个框架图:
    设计模式:设计自己的MVC框架(图二)

    点击查看大图

     

  • 相关阅读:
    NanoProfiler
    NanoProfiler
    Open Source Cassandra Gitbook for Developer
    Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理
    Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复
    Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误
    Android Fragment使用(一) 基础篇 温故知新
    Set up Github Pages with Hexo, migrating from Jekyll
    EventBus源码解析 源码阅读记录
    Android M Permission 运行时权限 学习笔记
  • 原文地址:https://www.cnblogs.com/cuihongyu3503319/p/658532.html
Copyright © 2011-2022 走看看