當柳上原的風吹向天際的時候...

          真正的快樂來源于創造

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks

          我們知道,在Struts1.x中,大致的內部處理流程是這樣的:ActionServlet作為中央處理器,它配置在Web.xml中,接受所有*.do的請求,然后解析URI,得到*.do中的*部分,即path,然后根據path在struts-config.xml中找到具體處理業務的Action以及與之配套的ActionForm和ActionForward,再根據Action的type用Java反射機制生成Action的實例進行具體業務處理,如果Action有ActionForm也會用反射機制生成form傳遞到函數execute中,該函數返回一個ActionForward對象給ActionServlet,如果它不是空,ActionServlet會從中取出URl進行頁面跳轉,否則不采取行動。

          實際上,要拋開Struts框架,自己實現這樣一套流程并不很困難,利用Servlet,正則表達式,XML解析,反射和BeanUtils的相關知識就能實現一個。這樣不但能鞏固相關知識點的掌握,也能對Struts框架有進一步的理解。下面就是我按自己的理解完成的Struts內部流程的具體實現,事先聲明一下,Struts的源碼我沒有看過,其中具體的處理可能和其原本的處理不一致,有些能簡化的部分就簡化了,文中處理不正確的地方還請方家指出。

          1.ActionServlet的配置。
          ActionServlet是一個Servlet,它是Struts框架中的中央控制器,在App啟動時即載入,所有*.do的請求都會發給它,然后再由它派發給具體的業務處理Action,在我的模擬實現工程中,它是這樣配置的。

          <?xml version="1.0" encoding="UTF-8"?>
          <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
              xmlns:xsi
          ="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation
          ="http://java.sun.com/xml/ns/j2ee 
              http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
          >

              
          <!-- 首頁 -->
              
          <welcome-file-list>
                  
          <welcome-file>/web/page/first.jsp</welcome-file>
              
          </welcome-file-list>

              
          <!-- 總控Servlet -->
              
          <servlet>
                  
          <servlet-name>DispatchServlet</servlet-name>
                  
          <servlet-class>
                      com.heyang.action.ActionServlet
                  
          </servlet-class>
                  
          <init-param>
                      
          <description>Configuration File</description>
                      
          <param-name>configFile</param-name>
                      
          <param-value>web-inf\struts-config.xml</param-value>
                  
          </init-param>
                  
          <load-on-startup>0</load-on-startup>
              
          </servlet>

              
          <servlet-mapping>
                  
          <servlet-name>DispatchServlet</servlet-name>
                  
          <url-pattern>*.do</url-pattern>
              
          </servlet-mapping>
          </web-app>

          在這個Web.xml中,在servlet-mapping節點設置了所有*.do的請求都發給com.heyang.action.ActionServlet進行處理;init-param節點指定了配置文件Struts-conifg.xml的位置;在load-on-startup節點中,讓這個Servlet變成自啟動Servlet,這樣設置后,一旦WebApp被載入容器,它的init方法就會被容器調用,在這個方法中,我們要執行重要的處理--讀取配置文件struts-config.xml.

          2.配置文件的讀取。
          前面說了,ActionServlet的init方法中要讀取配置文件,讀取之前當然要找到文件的物理位置,這不難,先取得Webapp的物理路徑再加上param-value指定的相對路徑即可,代碼如下:

          public class ActionServlet extends HttpServlet {
              
          private static final long serialVersionUID = 56890894234786L;

              
          public void doPost(HttpServletRequest request, HttpServletResponse response)
                      
          throws ServletException, java.io.IOException {
                ..
              }

                  
              
          public void doGet(HttpServletRequest request, HttpServletResponse response)
                      
          throws ServletException, java.io.IOException {
                  doPost(request, response);
              }

              
              
          /**
               * 初始化,當容器加載Servlet時會首先調用這個函數
               * 因為設置了Servlet的load-on-startup部分
               
          */

              
          public void init() throws ServletException {
                  
          // 通過ServletContext取得工程的絕對物理路徑
                  ServletContext sct = getServletContext();
                  String realPath 
          = sct.getRealPath("/");
                  
                  
          // 通過ServletConfig實例取得初始化參數configFile
                  ServletConfig config=this.getServletConfig();        
                  String strutsCfgFile
          =config.getInitParameter("configFile");
                  
                  
          // 組合配置文件全物理路徑
                  strutsCfgFile=realPath+strutsCfgFile;
                  System.out.println(
          "配置文件Struts-config.xml全物理路徑是"+strutsCfgFile);
                  
                  
          // 讀入配置文件
                  StrutsConfiger.readConfigFile(strutsCfgFile);
              }

          }


          最后,將配置文件的全路徑裝配好后,交給StrutsConfiger類的靜態函數readConfigFile進行處理。

          StrutsConfiger類的readConfigFile的主要功能是從struts-config.xml讀出Action及與之配套的ActionForm和ActionForward的信息,然后存放在一個哈希表中。代碼如下:

          /**
               * 讀取filePathname指定的配置文件,找出其中Action,ActionForm和ActionForwad的信息
               * 放入一個ActionBean對象中,最后將這些對象放入哈希表actionMap
               * 
          @param filePathname
               
          */

              
          public static void readConfigFile(String filePathname){
                  
          try {
                      File file 
          = new File(filePathname);
                      SAXReader reader 
          = new SAXReader();
                      Document document 
          = reader.read(file);
                      Element root 
          = document.getRootElement();
                      
                      
          // 找到Action將要用到的ActionForm
                      Element formBeansElm=root.element("form-beans");            
                      List formNodes
          =formBeansElm.elements("form-bean");    
                      
                      Map
          <String,String> formMap=new HashMap<String,String>();
                      
                      
          for(Iterator it=formNodes.iterator();it.hasNext();){
                          Element formElm
          =(Element)it.next();
                          
                          formMap.put(formElm.attributeValue(
          "name"), formElm.attributeValue("type"));
                      }

                      
                      
          // 找到各Action及其下的ActionForward
                      actionMap =new Hashtable<String,ActionBean>();
                      
                      Element actionMappingsElm
          =root.element("action-mappings");            
                      List actionNodes
          =actionMappingsElm.elements("action");    
                      
                      
          for(Iterator it=actionNodes.iterator();it.hasNext();){
                          Element actionElm
          =(Element)it.next();
                          ActionBean actionBean
          =new ActionBean();
                          
                          actionBean.setPath(actionElm.attributeValue(
          "path"));
                          
                          actionBean.setType(actionElm.attributeValue(
          "type"));
                          
                          String formName
          =actionElm.attributeValue("name");
                          
          if(formName!=null){
                              actionBean.setName(formName);
                              actionBean.setFormType(formMap.get(formName));
                          }

                          
                          
          // Forward部分
                          try{
                              List forwardNodes
          =actionElm.elements("forward");    
                              
          for(Iterator iter=forwardNodes.iterator();it.hasNext();){
                                  Element forwardElm
          =(Element)iter.next();
                              
                                  
          if(forwardElm!=null){
                                      ForwardBean bean
          =new ForwardBean();
                                      bean.setName(forwardElm.attributeValue(
          "name"));
                                      bean.setPath(forwardElm.attributeValue(
          "path"));
                                      
                                      actionBean.addForward(bean);
                                  }

                              }

                          }

                          
          catch(Exception ex){
                              ;
          // 如果這里發生異常,說明Action節點中沒有forward子節點
                          }

                          
                          
                          actionMap.put(actionBean.getPath(), actionBean);
                          System.out.println(actionBean);
                      }

                  }
           catch (Exception ex) {
                      
          throw new ReadStrutsCofigException("在使用dom4j閱讀解析文檔struts-config.xml時發生異常");
                  }
                  
              }


          這段代碼就是利用dom4j在Dom找到相應節點,有些特殊的地方如Action中可以有多個Forward或沒有Forword需要注意一下,做一點特殊處理。

          該函數執行完成后,配置文件的Action信息就被讀入了StrutsConfiger的靜態屬性actionMap中,在處理具體用戶請求時我們將會用到它。

          3.將用戶請求發給具體的Action。
          在Web.xml中,所有*.do的請求都被發給了ActionServlet,我們需要解析出*部分,再根據它找到具體處理的Action。解析使用正則表達式即可,代碼如下:

                  // 取得請求的URI
                  String reqUri=request.getRequestURI();
                  
                  
          // 取得模式匹配字符串,即go,do等
                  String patternStr;
                  
          if(reqUri.contains("?")){
                      patternStr
          =StringUtil.getMatchedString("([.])(.*)?",reqUri);
                  }

                  
          else{
                      patternStr
          =StringUtil.getMatchedString("([.])(.*)$",reqUri);
                  }


                  
          // 取得請求的path
                  String requestPath=StringUtil.getMatchedString("/(.*)(/.*)[.]"+patternStr,reqUri);        
                  System.out.println(
          "請求的path是"+requestPath);

           輔助函數getMatchedString:

          public static String getMatchedString(String regex,String text){
                  Pattern pattern
          =Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
                  
                  Matcher matcher
          =pattern.matcher(text);
           
                  
          while(matcher.find()){
                      
          return matcher.group(2);
                  }

                  
                  
          return null;
              }


          解析出requestPath之后,我們使用StrutsConfiger類的函數getActionBean就可以找到與之對應的ActionBean,在這個bean中,負責具體進行處理的Action路徑,ActionForm名和路徑,ActionForward等信息都包含到了這個bean中。

          接下來按流程進行處理,下面的代碼中注釋寫得很清楚,這里就不贅述了。值得一提的是在向ActionForm的屬性賦值時,Apche的工具類BeanUtils的使用,有了它的幫助,向一個實例的屬性賦值頓時變得輕松容易起來,全要自己靠反射去實現就太耗功夫了。

          // 找到處理這個請求的Action
                  ActionBean actionBean=StrutsConfiger.getActionBean(requestPath);
                  
          if(actionBean==null){
                      
          throw new CannotFindActionException("找不到path"+requestPath+"對應的Action");
                  }
                  
                  System.out.println(
          "處理請求的Action具體信息是"+actionBean);
                  
                  
          // 通過反射調用真正的Action類進行處理
                  String actionClassName=actionBean.getType();
                  
                  
          try {
                      Class cls 
          = Class.forName(actionClassName);
                      Action action 
          = (Action)cls.newInstance();
                      
                      
          // 找到Action中的Form
                      ActionForm form=null;
                      
          if(actionBean.getFormType()!=null){
                          Class cls2 
          = Class.forName(actionBean.getFormType());
                          form
          =(ActionForm)cls2.newInstance();
                          
                          
          // 取得輸入參數并存入哈希表
                          Enumeration params=request.getParameterNames();        
                          
          while(params.hasMoreElements()){
                              String key
          =(String)params.nextElement();            
                              String value
          =request.getParameter(key);
                              
                              BeanUtils.setProperty(form, key, value);
                          }
                          
                      }

                  
                      
          // 執行業務操作
                      ActionForward actionForward = action.execute(StrutsConfiger.getMappin(requestPath), form, request, response);
                  
                      
          // 根據返回的actionForward翻頁
                      if(actionForward!=null){
                          RequestDispatcher dispatcher 
          = request.getRequestDispatcher(actionForward.toString());
                          dispatcher.forward(request, response);
                          
          return;
                      }
                      
                  }

                  
          catch (ClassNotFoundException e) {
                      
          throw new ReflectionException("無法通過反射的方式生成"+actionClassName+"的實例");
                  }
           
                  
          catch (Exception e) {
                      
          throw new UnexpectedException("未預料到的異常"+e.getMessage());
                  }

          到這里,主要工作就已經完成了。

          4.具體的Action示例。

          下面是有ActionForm的Action。

          package com.heyang.action;

          import java.util.LinkedHashMap;
          import java.util.Map;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          import com.heyang.action.base.Action;
          import com.heyang.action.base.ActionForm;

          /**
           * 用于登錄處理的Servlet
           * 
          @author sitinspring
           *
           * @date 2008-2-12
           
          */

          public class LoginAction extends Action {
              
          public ActionForward execute(ActionMapping mapping, ActionForm form,
                      HttpServletRequest request, HttpServletResponse response)
                      
          throws Exception {
                  request.setCharacterEncoding(
          "UTF-8");
                  
                  Map
          <String,String> ht=new LinkedHashMap<String,String>();
                  
                  LoginForm inform
          =(LoginForm)form;
                      
                  ht.put(
          "Name:", inform.getName());
                  ht.put(
          "Pswd:", inform.getPswd());
                  
                  request.setAttribute(
          "ht", ht);

                  
          return mapping.findForward("loginResult");
              }

          }

           下面是不需要ActionForm的Action:

          package com.heyang.action;

          import java.util.LinkedHashMap;
          import java.util.Map;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          import com.heyang.action.base.Action;
          import com.heyang.action.base.ActionForm;

          /**
           * 用于頁面跳轉處理的Servlet
           * 
          @author sitinspring
           *
           * @date 2008-2-12
           
          */

          public class ShowPageAction extends Action {
              
          private static final long serialVersionUID = 56890894234786L;

              
          public ActionForward execute(ActionMapping mapping, ActionForm form,
                      HttpServletRequest request, HttpServletResponse response)
                      
          throws Exception {
                  request.setCharacterEncoding(
          "UTF-8");
                  
                  String pageName
          =request.getParameter("page");
                  
                  Map
          <String,String> ht=new LinkedHashMap<String,String>();
                  
                  ht.put(
          """");
                  
                  request.setAttribute(
          "ht", ht);

                  
          return new ActionForward("/web/page/"+pageName+".jsp");
              }

          }


          可以看出,這和Struts的Action寫法幾乎是完全一樣的。

          到這里,Struts內部流程模擬實現的主要細節就寫完了,有些代碼在工程里,如果有興趣可以下載來看看。

          Sturts模擬工程代碼下載:
          http://www.aygfsteel.com/Files/heyang/StrutsFlowSimulator.rar

          posted on 2009-02-12 15:12 何楊 閱讀(3979) 評論(1)  編輯  收藏

          Feedback

          # re: Struts內部流程(總控ActionServlet讀取Struts-config.xml后,將請求轉發到諸負責業務處理的Action流程)模擬實現 2012-08-17 15:26 葉楚鑫
          謝謝樓主分享,之前對這問題一直處于半疑半懂,看完很受用,謝謝啊。  回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 永春县| 肥东县| 天柱县| 七台河市| 平乡县| 二连浩特市| 东至县| 湛江市| 黑水县| 兴仁县| 磐石市| 安图县| 瑞安市| 故城县| 称多县| 留坝县| 丘北县| 寿宁县| 芷江| 凤冈县| 海宁市| 泸水县| 景泰县| 盐边县| 禹城市| 赞皇县| 白玉县| 阿拉善盟| 陕西省| 林口县| 怀化市| 甘泉县| 望都县| 百色市| 大连市| 垣曲县| 乌海市| 扎鲁特旗| 建德市| 建阳市| 翼城县|