自己写一个MVC框架(三)

自从上一篇之后,隔了好久才写这篇真是不好意思。下面我把这个MVC框架的剩余的最后一部分分享给大家。

    MVC里面不仅需要action这样普通的控制器,还需要另外一种控制器:前端控制器ActionServlet
    ActionServlet 继承了传统的servlet,负责从创建应用命令控制器RequestProcessor,和创建XML解析器XmlParser,它如同打仗时的先锋队,“所有的请求”首先被它获取拦截,通过它和已知的配置文件就可以把请求发给对应的action,
因此它在MVC模型中扮演中央控制器的角色。(其实和struts框架中的actionservlet的功能几乎一样)代码如下:
   

  1. public class ActionServlet extends HttpServlet {  
  2.   
  3.       
  4.     public void destroy() {  
  5.           
  6.     }  
  7.   
  8.       
  9.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  10.             throws ServletException, IOException {  
  11.         this.doPost(request, response);  
  12.     }  
  13.   
  14.       
  15.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  16.             throws ServletException, IOException {  
  17.         this.process(request, response);  
  18.     }  
  19.   
  20.     /**  
  21.      * 负责创建应用控制器RequestProcessor,并且缓存它  
  22.      * @param request  
  23.      * @param response  
  24.      */  
  25.     private void process(HttpServletRequest request, HttpServletResponse response) {  
  26.           
  27.         RequestProcessor processor = null;  
  28.           
  29.         if (null == this.getServletContext().getAttribute("REQUERS_PROCESSOR")) {  
  30.             //创建  
  31.             processor = new RequestProcessor(this.getServletContext());  
  32.               
  33.             this.getServletContext().setAttribute("REQUERS_PROCESSOR", processor);  
  34.               
  35.         } else {  
  36.             processor = (RequestProcessor) this.getServletContext().getAttribute("REQUERS_PROCESSOR");  
  37.         }  
  38.           
  39.         try {  
  40.             processor.processor(request, response);  
  41.         } catch (Exception e) {  
  42.             e.printStackTrace();  
  43.         }  
  44.           
  45.     }  
  46.       
  47.       
  48.     /**  
  49.      * 在初始化的时候,解析xml返回ActionMappingConfig对象,缓存对象  
  50.      */  
  51.     public void init() throws ServletException {  
  52.           
  53.         String tmp = this.getServletContext().getRealPath("/");  
  54.         String path = tmp + this.getInitParameter("config");  
  55.           
  56.         ActionMappingConfig config = XMLParser.getActionMappingConfig(path);  
  57.         this.getServletContext().setAttribute("config", config);  
  58.           
  59.     }  
  60.   
  61. }  

初始化的时候,我们需要解析xml并返回之前谈到过的ActionMappingConfig,并将其缓存到公共区域中,以便之后的读取。当然要想使这个servlet起作用还是需要在web。xml里面进行配置的,由于需要一开始就加载配置文件,所以说还要加上<load-on-startup>0</load-on-startup>;
  1. <servlet>  
  2.     <servlet-name>ActionServlet</servlet-name>  
  3.     <servlet-class>net.localer.mvc.servlet.ActionServlet</servlet-class>  
  4.       
  5.     <init-param>  
  6.         <param-name>config</param-name>  
  7.         <param-value>/WEB-INF/mvc.xml</param-value>  
  8.     </init-param>  
  9.     <load-on-startup>0</load-on-startup>  
  10.   </servlet>  




上面涉及到RequestProcessor这个类,这个类在MVC小框架中充当命令应用控制器。                                                                                                             它负责创建和缓存Action对象,同时也根据请求path调用action对象,并创建ActionForm对象,将ActionForm传递到Action对象中。详细的RequestProcessor类的处理过程我总结如下:
1、获取请求路径。
2、根据请求路径查找缓存(可以用一个Hashtabel来当缓存)中是否有对应的action实例,如果没有将对应的action实例化出来。
3、若是刚实例的action则把Action对象缓存起来。
4.  根据actionconfig查找对应action的actionForm。
5.  反射设置参数到actionForm。
6.  传递ActionForm到Action,并调用execute方法。
7.  接受action的返回值。
8.  根据返回值决定重定向还是转发。代码如下:

  1. public class RequestProcessor {  
  2.       
  3.       
  4.     private ServletContext application = null;  
  5.       
  6.     private Map<String,Action> actionInstance = null;  
  7.       
  8.     public RequestProcessor(ServletContext application) {  
  9.         this.application = application;  
  10.         actionInstance = new Hashtable<String, Action>();  
  11.     }  
  12.   
  13.   
  14.     /**  
  15.   
  16.      * @param request  
  17.      * @param response  
  18.      * @throws Exception  
  19.      */  
  20.     public void processor(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  21.         //1、获取请求路径  
  22.         String uri = request.getRequestURI();  
  23.         //2、去掉后缀的.do,比如请求的地址是http://127.0.0.1/mvc/index.do  
  24.         uriuri = uri.substring(0, uri.length() - 3);  
  25.         String path = application.getRealPath("/");  
  26.         String[] array = path.split("\\\\");  
  27.         uriuri = uri.substring(array[array.length - 1].length() + 1);  
  28.           
  29.           
  30.         //3、获取缓存ActionMappingConfig  
  31.         ActionMappingConfig mapping = (ActionMappingConfig)application.getAttribute("config");  
  32.           
  33.           
  34.         //4、把路径当成key获取对应的ActionConfig对象  
  35.         ActionConfig actionConfig = mapping.getActionConfig(uri);  
  36.           
  37.         //5、判断acttionConfig对象是否存在,为空就是配置文件中没有这个路径的配置  
  38.         if (null == actionConfig) {  
  39.             throw new Exception("没有找到对应的action实例");  
  40.         }  
  41.           
  42.         //从缓存获取action对象  
  43.         Action action = actionInstance.get(uri);  
  44.           
  45.         //6、根据路径信息到缓存中查找Action对象  
  46.         if (null == action) {  
  47.               
  48.             //7、反射出该ActionConfig对象对应的Action对象  
  49.             action = this.getActionInstance(actionConfig);  
  50.               
  51.             //8、把新创建的action对象缓存  
  52.             actionInstance.put(uri, action);  
  53.         }  
  54.           
  55.           
  56.         //9、获取到action配置的name属性的值  
  57.         String name = actionConfig.getName();  
  58.           
  59.         ActionForm form = null;  
  60.           
  61.         if (name != null) {  
  62.               
  63.             //10、根据ActionConfig的name属性找到对应的FormBeanConfig对象  
  64.             FormBeanConfig config = mapping.getFormBeanConfig(name);  
  65.               
  66.             //11、获取对应ActionForm的实例  
  67.             form = this.getActionFormInstance(config);  
  68.               
  69.             //12、封装界面上传递的参数到ActionForm里面  
  70.             this.setParamter(form, request);  
  71.         }  
  72.           
  73.           
  74.         //13、调用action的execute方法,传递form对象  
  75.         ActionForward forward = action.execute(form, request, response);  
  76.           
  77.         //14、做转发或者重定向  
  78.         this.forward(forward, request, response);  
  79.     }  
  80.       
  81.     /**  
  82.      * 这个方法是决定重定向或者转发对应视图的  
  83.      * @param forward  
  84.      * @param request  
  85.      * @param response  
  86.      * @throws ServletException  
  87.      * @throws IOException  
  88.      */  
  89.     private void forward(ActionForward forward,HttpServletRequest request, HttpServletResponse response)  
  90.     throws ServletException, IOException  {  
  91.         if (null == forward) {  
  92.             return;  
  93.         }  
  94.           
  95.         if (forward.isForward()) {  
  96.             //转发  
  97.             request.getRequestDispatcher(forward.getUri()).forward(request, response);  
  98.               
  99.         } else {  
  100.             //重定向  
  101.             response.sendRedirect(forward.getUri());  
  102.         }  
  103.           
  104.     }  
  105.       
  106.     /**  
  107.      * 反射自动封装参数  
  108.      * @param form  
  109.      * @param request  
  110.      */  
  111.     private void setParamter(ActionForm form, HttpServletRequest request)  {  
  112.         //1、获取界面上传递过来的所有的参数的名字  
  113.         Enumeration<String> enu = request.getParameterNames();  
  114.           
  115.         String parameterName = null;  
  116.         Class c = form.getClass();  
  117.         Field[] fields = null;  
  118.         StringBuffer setMethodName = null;  
  119.         Method setMethod = null;  
  120.           
  121.         Map<String,String> map = new HashMap<String,String>();  
  122.           
  123.         //2、迭代所有名字,判断和form当中是否有名字一致的  
  124.         while (enu.hasMoreElements()) {  
  125.               
  126.             setMethodName = new StringBuffer();  
  127.             parameterName = enu.nextElement();  
  128.               
  129.             if (map.get(parameterName) != null) {  
  130.                 continue;  
  131.             }  
  132.               
  133.             //获取form所有的字段  
  134.             fields = c.getDeclaredFields();  
  135.               
  136.             for (int i = 0; i < fields.length; i++) {  
  137.                   
  138.                 //如果有一个参数名字和字段名字一模一样  
  139.                 if (fields[i].getName().equals(parameterName)) {  
  140.                       
  141.                     //拼凑出set方法  
  142.                     setMethodName.append("set").append(parameterName.substring(0, 1).toUpperCase()).append(parameterName.substring(1));  
  143.                       
  144.                       
  145.                     //获取对应的set方法  
  146.                     try {  
  147.                         setMethod = c.getMethod(setMethodName.toString(), new Class[]{fields[i].getType()});  
  148.                           
  149.                         //判断form里面的字段是数组的还是String类型的  
  150.                         if (fields[i].getType().getName().equals("[Ljava.lang.String;")) {  
  151.                             map.put(parameterName, "");  
  152.                               
  153.                             setMethod.invoke(form, new Object[]{request.getParameterValues(parameterName)});  
  154.                         } else {  
  155.                             setMethod.invoke(form, new Object[]{request.getParameter(parameterName)});  
  156.                         }  
  157.                           
  158.                     } catch (SecurityException e) {  
  159.                         e.printStackTrace();  
  160.                     } catch (NoSuchMethodException e) {  
  161.                         e.printStackTrace();  
  162.                     } catch (IllegalArgumentException e) {  
  163.                         e.printStackTrace();  
  164.                     } catch (IllegalAccessException e) {  
  165.                         e.printStackTrace();  
  166.                     } catch (InvocationTargetException e) {  
  167.                         e.printStackTrace();  
  168.                     }  
  169.                       
  170.                 }  
  171.                   
  172.             }  
  173.               
  174.         }  
  175.           
  176.           
  177.     }  
  178.       
  179.     /**  
  180.      * 反射从FormBeanConfig中获取ActionForm对象  
  181.      * @param config  
  182.      * @return  
  183.      */  
  184.     private ActionForm getActionFormInstance(FormBeanConfig config) {  
  185.         ActionForm form = null;  
  186.         try {  
  187.             form = (ActionForm)Class.forName(config.getType()).newInstance();  
  188.         } catch (ClassNotFoundException e) {  
  189.             e.printStackTrace();  
  190.         } catch (InstantiationException e) {  
  191.             e.printStackTrace();  
  192.         } catch (IllegalAccessException e) {  
  193.             e.printStackTrace();  
  194.         }  
  195.           
  196.         return form;  
  197.     }  
  198.       
  199.       
  200.     /**  
  201.      * 反射出ActionConfig中Action对象  
  202.      * @param config  
  203.      * @return  
  204.      */  
  205.     private Action getActionInstance(ActionConfig config) {  
  206.         Action action = null;  
  207.         try {  
  208.               
  209.             action = (Action)Class.forName(config.getType()).newInstance();  
  210.         } catch (ClassNotFoundException e) {  
  211.             e.printStackTrace();  
  212.         } catch (InstantiationException e) {  
  213.             e.printStackTrace();  
  214.         } catch (IllegalAccessException e) {  
  215.             e.printStackTrace();  
  216.         }  
  217.           
  218.         return action;  
  219.     }  
  220.       
  221.       
  222. }  
上述代码的注释也把整个流程描述得十分清楚。有很多代码细节大家若不懂,百度一下就能知道,是一些很简单的小细节功能。

加上之前的类,一个简单的MVC框架就基本成型了。下面是简单的应用代码:

  1. public class NewsAction extends Action {  
  2.   
  3.     @Override  
  4.     public ActionForward execute(ActionForm form, HttpServletRequest request,  
  5.             HttpServletResponse response) throws Exception {  
  6.         NewsForm newsForm = (NewsForm)form;  
  7.           
  8.         return new ActionForward("index.jsp",false);  
  9.     }  
  10.   
  11. }  
这个Action就继承了抽象类Action并对其中的execute方法进行重写。
  1. public class NewsForm extends ActionForm{  
  2.     private String id;  
  3.     private String title;  
  4.     private String content;  
  5.     private String[] abc;  
  6.       
  7.       
  8.       
  9.     public String[] getAbc() {  
  10.         return abc;  
  11.     }  
  12.     public void setAbc(String[] abc) {  
  13.         this.abc = abc;  
  14.     }  
  15.     public String getId() {  
  16.         return id;  
  17.     }  
  18.     public void setId(String id) {  
  19.         this.id = id;  
  20.     }  
  21.     public String getTitle() {  
  22.         return title;  
  23.     }  
  24.     public void setTitle(String title) {  
  25.         this.title = title;  
  26.     }  
  27.     public String getContent() {  
  28.         return content;  
  29.     }  
  30.     public void setContent(String content) {  
  31.         this.content = content;  
  32.     }  
  33.       
  34.       
  35. }  
这个form继承了actionform用来封装页面传来的参数。之前的mvc.xml配置如下:
  1. <!-- form-beans标签只有一个 -->  
  2.     <form-beans>  
  3.         <!-- form-bean标签有多个 -->  
  4.         <form-bean name="newsForm" type="net.localer.news.form.NewsForm"/>  
  5.           
  6.         <form-bean name="userForm" type="net.localer.news.form.UserForm"/>  
  7.           
  8.     </form-beans>  
  9.       
  10.     <!-- action-mappings标签只有一个 -->  
  11.     <action-mappings>  
  12.         <!--   
  13.             action元素:配置业务Action类  
  14.               
  15.             path : 请求的路径  
  16.               
  17.             type : 业务Action类的类路径  
  18.           
  19.          -->  
  20.         <!-- action标签有多个 -->  
  21.         <action path="/index" type="net.localer.news.action.NewsAction" name="newsForm"/>  
  22.           
  23.         <action path="/user" type="net.localer.news.action.UserAction" name="userForm"/>  
  24.   
  25.     </action-mappings>  


项目运行起来之后,在页面输入http://127.0.0.1/项目名/index.do 请求就会被拦截并调用NewsAction中的execute方法。
(具体项目的具体设置不一样,struts框架原则上认为访问任何一个页面之前都需要通过一个控制器,之后再转发到对应的页面,因此我们用户在地址栏上输入的时候一般不会出现*.jsp这样的地址,而一般都是*.do或者*.action等等这样的地址)。

最后呢我们可以把之前的代码打成一个jar包(不包括NewsAction)。下次写一个新的web项目的时候我们就可以把这个jar包导入,自己建一个xml文件来写配置(和上面的配置文件一样),省去了写大量servlet配置的时间,这样的感觉就如同用一个框架,虽然这个框架很小,嘿嘿,总之算是实现了一个MVC框架的最最基本的功能。当然呢我这个框架还有很多很多的不足,其中我认为最大最大的不足呢是:
1.dispatcherAction里面的那个method参数不能自己配置,而是写死了,这样页面传递参数的时候会受到了限制。
2.没有像标准struts框架那样把转发重定向的路径 也配置到文件(如<result name="hello">/index.jsp</result>),这样有时候路劲变化的时候还是需要改变源代码,违背了开闭原则。
这个框架和标准的框架没得比,但是也把MVC的基本思想体现出来,在下分享出来后,希望大家多多交流,我也希望通过这个来学习更多的架构知识,当然本人的实力实在有限,文字表达功底也不大行,所有的不足,望大家包容。
                             那这样吧,嘿嘿!

猜你喜欢

转载自lixinstudio.iteye.com/blog/1293631