posts - 3, comments - 1, trackbacks - 0, articles - 5

          簡要的對struts ActionServlet init()方法以及 struts RequestProcessor類做源碼分析

          首先要確定的是Servlet至始至終只有一個對象以及init()方法會在Servlet第一次加載時被執行一次也是唯

          一的一次,所以Servlet的初始化工作一般在init()方法進行

          一、解析ModuleConfig
          ModuleConfig封裝了struts-config的所有配置信息
          actionConfigs(ActionMapping)/actionConfigList 、exceptions 、formBeans(ActionForm)、forwards

          (ActionForward)、messageResources、plugIns等
          // 初始化ModuleConfig配置工廠
          initModuleConfigFactory();
          // 由配置工廠實例化一個ModuleConfig的對象
          ModuleConfig moduleConfig = initModuleConfig("", config);
          initModuleConfig(String prefix, String paths)方法做了一下事情
          1、 ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
          由生成的配置工廠生成一個工廠實例
          2、ModuleConfig config = factoryObject.createModuleConfig(prefix);
          創建ModuleConfig實例createModuleConfig()方法會有DefaultModuleConfigFactory 執行
          這是在配置文件中指定的工廠類
          方法內部new 出ModuleConfig對象
          在ModuleConfig構造方法內對以下參數進行了初始化
           

          this.prefix = prefix;
                  
          this.actionConfigs = new HashMap();  //ActionMapping集合
                  this.actionConfigIds = new HashMap();
                  
          this.actionConfigList = new ArrayList();
                  
          this.actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
                  
          this.actionMappingClass = "org.apache.struts.action.ActionMapping";
                  
          this.actionForwardClass = "org.apache.struts.action.ActionForward";
                  
          this.configured = false;
                  
          this.controllerConfig = null;
                  
          this.exceptions = new HashMap();
                  
          this.formBeans = new HashMap();   //ActionForm集合
                  this.forwards = new HashMap();   //ActionForward集合
                  this.messageResources = new HashMap();
                  
          this.plugIns = new ArrayList();

           

          到此config構造完成
          Digester digester = initConfigDigester();
          initConfigDigester()方法添加解析struts文件的解析規則

          3、循環struts配置文件并把所有標簽封裝為相應對象填充到config對象相應的map集合中中
          List urls = splitAndResolvePaths(paths);
                  URL url;
                  for (Iterator i = urls.iterator(); i.hasNext();) {
                      url = (URL) i.next();
                      digester.push(config);
                      this.parseModuleConfigFile(digester, url);
                  }
          最后getServletContext().setAttribute(Globals.MODULE_KEY
                      + config.getPrefix(), config);
          把ModuleConfig設入ServletContext中
          至此config初始化工作全部完成

          struts每個模塊都對應一個ModuleConfig對象個,在init方法內被初始化,內部封裝了模塊xml文

          件中的配置


          二、struts核心類RequestProcessor類解析
          ActionServlet做為前端控制器當有請求被接收時,會調用process(request, response)方法
          1、ModuleConfig config = getModuleConfig(request);
          通過Servlet上下文找到ModuleConfig對象
          2、RequestProcessor processor = getProcessorForModule(config);
          通過Servlet上下文找到RequestProcessor對象  --所以RequestProcessor類也是單例的
          if (processor == null) {
                      processor = getRequestProcessor(config);
               }
          如果為空 也就說明是服務器端第一次接收客戶端的接收請求
          那么執行getRequestProcessor(config)方法生成RequestProcessor并設入ServletContext里
          此方法為同步方法,借此我猜測此方法為生成RequestProcessor的唯一方法
          方法內部對RequestProcessor調用了init()方法,進行了RequestProcessor的初始化并設入ServletContext

          里。
          為什么RequestProcessor要做成單例,原因就是RequestProcessor類內部有
          HashMap actions = new HashMap();
          這樣一個集合封裝了所有的控制器。
          這樣的設計是面向對象的。當ActionServlet接收到請求時它需要對請求分發到相應控制器中
          此時它獲取中央控制器,而這個中央控制器內部擁有所有控制器的引用。
          /*
          這里我的疑惑是為什么要通過反射生成RequestProcessor,此類并沒有繼承實現任何接口或類。
          process方法最后調用了RequestProcessor的process()方法。此方法為RequestProcessor的核心方法
          */原因經過學習發現RequestProcessor并不是單例的,而實際因為struts的多模塊應用模式,導致

          RequestProcessor類是多例的

          三、解析RequestProcessor類核心方法process
          1、 String path = processPath(request, response);
          截取客戶端請求字符串相當于<action>標簽的path屬性
          2、ActionMapping mapping = processMapping(request, response, path);
          獲取控制器相對的ActionMapping 對象
           processMapping(request, response, path)
           1、ActionMapping mapping =
                      (ActionMapping) moduleConfig.findActionConfig(path);
           方法內部調用了moduleConfig.findActionConfig(path);
           ModuleConfig對象前面已做過解析,在findActionConfig方法內部已path屬性做為Key值,直接 

           到ActionConfigHashMap集合內尋找ActionMapping,原因見一.3
           2.if (mapping != null) {
                      request.setAttribute(Globals.MAPPING_KEY, mapping);

                      return (mapping);
                  }
           如果找到mapping那么放入request中,并返回
           3、 ActionConfig[] configs = moduleConfig.findActionConfigs();

                  for (int i = 0; i < configs.length; i++) {
                      if (configs[i].getUnknown()) {
                          mapping = (ActionMapping) configs[i];
                          request.setAttribute(Globals.MAPPING_KEY, mapping);

                          return (mapping);
                      }
                  }
           //ActionMapping說明,和Action的unknow屬性有關
          3、if (mapping == null) {
                      return;
                  }
          容錯處理,說明客戶端請求的path路徑并沒有被配置
          4、ActionForm form = processActionForm(request, response, mapping);
           processActionForm(request, response, mapping);
           1、ActionForm instance =
                      RequestUtils.createActionForm(request, mapping, moduleConfig,
                          servlet);
            1、String name = mapping.getName();
            獲得mapping name屬性也就是映射的相對ActionForm的name
            2、FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
            已name做為Key值在緩存的HashMap內獲取相對(FormBeanConfig)對象
            
            <!--對這里的一些繼承關系做下說明
            struts Config類的基類 BaseConfig
            ForwardConfig從BaseConfig繼承
            FormBeanConfig從BaseConfig繼承
            ActionConfig從BaseConfig繼承
            ActionMapping從ActionConfig繼承
            -->
            
            3、if (config == null) {
                        log.warn("No FormBeanConfig found under '" + name + "'");
                        return (null);
                   }
            如果為空也就說明并沒有配置相對ActionForm,而這是合法的所以返回空
            4、ActionForm instance =
                       lookupActionForm(request, attribute, mapping.getScope());
            此方法試圖在request,session內尋找ActionForm
            5、if ((instance != null) && config.canReuse(instance)) {
                        return (instance);
                   }
            如果找到那么返回
            6、return createActionForm(config, servlet);
            如果沒有找到則進行創建,方法傳遞了config,因為config內封裝了formbean標簽的所 

           有配置信息。方法可以根據類名進行反射生成對象。
           2、if ("request".equals(mapping.getScope())) {
                      request.setAttribute(mapping.getAttribute(), instance);
                  } else {
                      HttpSession session = request.getSession();

                      session.setAttribute(mapping.getAttribute(), instance);
                  }
           根據配置信息,把ActionForm設入相應作用域內
          此方法作用就是獲取ActionForm
          5、processPopulate(request, response, form, mapping);
          此方法用于做ActionForm的數據收集工作
           1、form.reset(mapping, request);
           此方法用于做數據重置,程序員可以重寫此方法
           2、RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
                      request);
           調用工具類進行數據收集
            1、if ((contentType != null)
                       && (contentType.startsWith("multipart/form-data"))
                       && (method.equalsIgnoreCase("POST"))) { 
            判斷是否是數據上傳
            2、if (!isMultipart) {
                       names = request.getParameterNames();
                   }
            如果不是獲取request里所有參數參數名。
            3、 while (names.hasMoreElements()) { 
                      String name = (String) names.nextElement(); //循環獲取參數名
                       String stripped = name;
                       Object parameterValue = null;
            ...
                       if (isMultipart) {   
                          parameterValue = multipartParameters.get(name);
                          parameterValue = rationalizeMultipleFileProperty(bean, name,    

           parameterValue);
                       } else {  //不是上傳 則獲取此參數名的所有參數值
                          parameterValue = request.getParameterValues(name);
                       }
                       if (!(stripped.startsWith("org.apache.struts."))) {
                          properties.put(stripped, parameterValue); 
                       }
                   }
            方法最后把參數名做為Key,所對應的所有參數值做為鍵值放入hashmap內
            5、BeanUtils.populate(bean, properties);
            由第三方工具類根據ActionForm以及所有參數名參數值進行數據收集
           3、if (!processValidate(request, response, form, mapping)) {
                          return;
                      }
           進行數據驗證,方法內部會調用ActionForm的validate方法,程序員可以重寫此方法
           
          6、Action action = processActionCreate(request, response, mapping);
          獲取控制器
           1、方法首先獲取Action的類名
           2、然后此方法對actions進行了同步處理,原因RequestProcessor是多例的,actions是一個成 

          員變量,緩存了所有action。
           3、instance = (Action) actions.get(className);

                      if (instance != null) {
                          if (log.isTraceEnabled()) {
                              log.trace("  Returning existing Action instance");
                          }

                          return (instance);
                      }
           試圖在集合內取出action,如果不為空 則返回。此時說明此Action已被執行過。
           4、instance = (Action) RequestUtils.applicationInstance(className);
           如果沒有反射創建Action引用。
           5、actions.put(className, instance);
           最后把action放入集合,并返回。
          7、ActionForward forward =
                      processActionPerform(request, response, action, form, mapping);
          此方法內部調用Action的execte方法,獲取轉向類。

           <!--說明類組織關系
           在他的父類里有 HashMap forwards = new HashMap();這樣一個集合
           這里的類組織關系是這樣的,ActionMapping從ActionConfig繼承,
           ActionConfig擁有forwards 集合,
           而ModuleConfig內部擁有ActionConfig集合,
           這和struts-config標簽的組織關系是完全類似的.
           ModuleConfig模塊配置類對應一個struts配置文件,
           ActionConfig控制器配置類對應一個Action標簽,
           ForwardConfig轉向配置類對應Forward標簽,
           而ModuleConfig在ActionServlet初始化時被加載。
           這里可以看出大師對類的組織的合理,每個標簽都有相應的類。
           -->

          綜上所述,由于forward標簽屬于ActionMapping標簽所以在程序里,能用findForward()方法找到

          ActionForward.
          而在ActionConfig內部有這樣一個方法addForwardConfig(ForwardConfig config);它已ForwardConfig 的

          name屬性做為Key,ForwardConfig 類實例做為值添加入緩存中。
          這也就是為什么我們可以在findForward()方法內通過傳name屬性獲取相應的ActionForward實例。

          8、processForwardConfig(request, response, forward);
          在process方法的最后調用了processForwardConfig()方法。完成轉向操作。
          在這個方法內部,他根據Redirect()是否設置為true選擇是進行轉發還是重定向。如果沒有設置,默認是進

          行轉發。

          說明:
                   此篇學習筆記算是閱讀尚學堂王勇老師struts視頻的讀后感吧,當時小弟剛學習struts,也算是個j2ee的新人就斗膽寫了這篇源碼的分析,今天發上來并未做任何刪減,錯誤是難免的,還希望各位大蝦閱讀時,多多指正,不吝賜教!!
                  最后感謝由衷的感謝王勇老師,感謝尚學堂!!

          主站蜘蛛池模板: 龙南县| 托里县| 蓝山县| 武陟县| 商丘市| 手游| 大邑县| 乌什县| 延边| 磴口县| 湘潭县| 墨脱县| 昂仁县| 寻甸| 临洮县| 泽库县| 邓州市| 禄丰县| 鹰潭市| 阿瓦提县| 西昌市| 遂昌县| 历史| 志丹县| 三明市| 蒲江县| 山阴县| 合山市| 遂川县| 绵阳市| 卢龙县| 桓台县| 沙田区| 湖口县| 全州县| 华坪县| 璧山县| 天气| 岢岚县| 嘉兴市| 泗洪县|