風行天下

          JAVA太極
          posts - 4, comments - 10, trackbacks - 0, articles - 55
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
          這么久才些好第二篇,說明我是一個很懶惰的人:)。第一篇文章雖然由很多人看,但是得到的反饋比較少,所以也不知道對大家有沒有用。同樣的,我的email為:mariah_fan@hotmail.com,希望大家多提意見。

          第二篇 struts的執(zhí)行(struts-1.1版)

          本篇詳細介紹struts在初始化之后是如何處理一個請求,并返回數(shù)據(jù)的。這里最核心的類是
          RequestProcessor以及RequestUtils。RequestProcessor類通過RequestDispatcher實現(xiàn)頁面的跳轉(zhuǎn),
          而RequestProcessor負責處理request中傳來的請求信息,存放到FormBeanConfig中,以及對要跳轉(zhuǎn)的
          url進行處理。

          struts 在初始化完成之后,會根據(jù)請求調(diào)用doGet(...)或者doPost(...)方法,這兩個方法直接
          調(diào)用process(request, response)方法。process(...)方法首先判斷當前的request屬于
          哪一個ModuleConfig,然后生成與這個ModuleConifg相對應的RequestProcessor,最后調(diào)用這個
          RequestProcessor的process(...)方法,執(zhí)行request的請求。

          一、RequestUtils.selectModule(String prefix, HttpServletRequest,ServletContext)方法:
          這個方法,根據(jù)prefix,從ServletContext中選擇相應的ModuleConfig,然后把這個ModuleConfig
          保存到request中。ServletContext對應的key值為Globals.MODULE_KEY + prefix,保存到request
          中使用的key值為Globals.MODULE_KEY。如果在ServletContext中不存在這樣的一個ModuleConfig,
          那么調(diào)用request.removeAttribute(Globals.MODULE_KEY)方法。然后以同樣的方法查找、保存
          MessageResources對象。當prefix為空時,會調(diào)用下面的方法選擇ModuleConfig。

          二、RequestUtils.selectModule(HttpServletRequest, ServletContext)
          這個方發(fā)首先使用getModuleName(HttpServletRequest, ServletContext)獲取相應的path,然后
          通過調(diào)用getModuleName(String matchPath, ServletContext)獲取相應的prefix。以這prefix為
          參數(shù)調(diào)用(一)中的selectModule(...)選擇ModuleConfig。
          獲取path的過程為:首先從request中查找名稱為
          INCLUDE_SERVLET_PATH(javax.servlet.include.servlet_path)的屬性,如果為空,就調(diào)用
          request.getServletPath()方法獲取servletPath。

          獲取prefix的過程為:它首先調(diào)用getModulePrefixes(ServletContext),獲取所有已存在的
          module前綴。 然后通過分析上面獲取的path來判斷當前的url屬于哪一個module,方法是截取
          path中最后一個"/"字符前面的字符串,然后與有上面方法中獲取的prefixes[]逐個對比,如果
          prefixes[]中存在這樣的一個值,則返回這個截取的字符串,否則繼續(xù)截取path最后面的"/"前面
          的字符串,然后對比。如果始終找不到,則返回""。

          getModulePrefixes(ServletContext)的執(zhí)行過程是:首先通過
          context.getAttribute(PREFIXES_KEY)查找是否存在這樣的一個保存所有的prefix的string array,
          如果存在,就說明已經(jīng)解析過一次了,就直接返回這個string array,否則遍歷ServletContext中
          所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)開頭的
          atrribute name,然后截取這個atrribute name中MODULE_KEY后面的字符串,作為一個prefix. 最
          后把通過這個這個方式獲取的所有的prefix作為一個ArrayList存儲到ServletContext中,屬性key
          值為PREFIXES_KEY(org.apache.struts.util.PREFIXES),這樣下次再次查找的時候就可以直接從這
          個attribute中獲取了。

          三、getModuleConfig(HttpServletRequest)
          這個方法就是獲取上面的selectModule(...)方法所得到的ModuleConfig。如果找不到這樣的
          ModuleConfig,那么就把ServletContext中缺省的ModuleConfig返回(調(diào)用
          getServletContext().getAttribute(Globals.MODULE_KEY))

          四、getRequestProcessor(ModuleConfig config)
          這個方法從根據(jù)ModuleConfig的prefix作為key,從ServletContext中獲取RequestProcessor。這
          個key值為Globals.REQUEST_PROCESSOR_KEY + config.getPrefix()。

          如果ServletContext中不存在這樣的一個RequestProcessor,那么就生成一個新的
          RequestProcessor的實例,完成初始化(保存ActionServlet以及ModuleConfig到這個新的實例中)后
          將其保存到ServletContext中。

          五、RequestProcessor.process(HttpServletRequest, HttpServletResponse)
          這是真正執(zhí)行HttpServletRequst請求的方法。

          這個方法首先判斷但前的HttpServletRequest是否是Multipart類型的request,也就是說當前
          的request是否有字段是"file"類型。這樣做的原因是這種類型的request中g(shù)etParameter()等
          相類似的方法都是無法執(zhí)行的,所以要區(qū)分對待。如果是Multipart類型的,那么把這個request包
          裝成MultipartRequestWrapper類。

          MultipartRequestWrapper 與 HttpServletRequest不同的就是重新實現(xiàn)了
          setParameter(String name, String value),getParameter(String name),getParameterNames()
          以及getParameterValues(String name)方法。
          這些方法的思想是所有的變量名、值對都保存到一個HashMap里:key為變量名,value為變量值,但
          是注意value都是String[]格式的。當有一個新的值加入的時候,通過setParameter(...)
          方法,把值加到數(shù)組的最后。在setParameter(...)中有一個比較少見的保存值的方法,記錄如下:
          public void setParameter(String name, String value) {
          String[] mValue = (String[]) parameters.get(name);
          if (mValue == null) {
          mValue = new String[0];
          }
          String[] newValue = new String[mValue.length + 1];
          System.arraycopy(mValue, 0, newValue, 0, mValue.length);
          newValue[mValue.length] = value;

          parameters.put(name, newValue);
          }

          然后是調(diào)用processPath(HttpServletRequest, HttpServletResponse)獲取地址,以這個地址作為
          從ModuleConfig獲取ActionMapping的Key。這里首先查詢
          request.getAttribute(INCLUDE_PATH_INFO)中是否有這樣的一個值,如果為空就調(diào)用
          request.getPathInfo()方法獲取path。如果這樣還是獲取不了,就從
          request.getAttribute(INCLUDE_SERVLET_PATH)方法中獲取path,找不到就使用
          request.getServletPath()得到的path進行分析。分析過程如下:

          然后如果這個path不是屬于當前的ModuleConfig的話,直接返回null。截取prefix后面的字符串,
          如果這個字符串以.XX結(jié)尾,那么就截取"."前面的字符,比如
          http://localhost:8080/servlet/where/go.do。where為module的名稱(prefix),那么我們獲取的
          path值為/go。

          然后是通過processLocale(HttpServletRequest ,HttpServletResponse)為當前用戶設(shè)定一個Local
          對象,它查找的順序是:moduleConfig.getControllerConfig().getLocale(),
          session.getAttribute(Globals.LOCALE_KEY),request.getLocale()。你可以在config XML文件
          中通過<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。

          調(diào)用processContent(HttpServletRequest, HttpServletResponse)為response設(shè)定contentType。
          這個contentType是從moduleConfig.getControllerConfig().getContentType()獲取的。你可以
          在config XML文件中通過<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。

          通過processNoCache(HttpServletRequest, HttpServletResponse)方法設(shè)置response的緩存。
          你可以在config XML文件中通過<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。

          processPreprocess(HttpServletRequest, HttpServletResponse)預處理request,這個方法是預
          留的,用戶可以根據(jù)自己的需要加一些預處理的程序。

          通過processMapping(HttpServletRequest, HttpServletResponse, String path)以
          processPath(...)的返回值為key從ModuleConfig中查找ActionMapping 對象。如果找到了,那么保
          存這個Mapping到request中,key值為Globals.MAPPING_KEY。如果不存在,那么遍歷ModuleConfig
          中所有的ActionMapping,查找哪一個是缺省的ActionMapping。然后把它保存到request中,key值
          為Globals.MAPPING_KEY。

          通過processRoles(HttpServletRequest,HttpServletResponse,ActionMapping)檢查當前用戶是
          否有權(quán)限執(zhí)行這個請求。如果request.isUserInRole(roles[i])返回true,則代表有。

          通過processActionForm(HttpServletRequest, HttpServletResponse, ActionMapping)生成一個
          ActionForm,然后根據(jù)ActionMapping所屬的scope,保存到request或者session中。key值為這個
          ActionMapping中的attribute屬性。ActionForm是通過RequestUtils.createActionForm(...)方法
          獲取的,在下一篇將會對這個方法進行詳細的說明。這里只說明ActionForm與FormBeanConfig以及
          FormPropertyConfig之間的區(qū)別。每個FormPropertyConfig代表Form表單的一個字段,表單中的所
          有FormPropertyConfig以一個HashMap保存到FormBeanConfig中。而FormBeanConfig是在
          Actionservet初始化的時候生成的:
          <form-beans>
          <form-bean name="一個名稱,作為action選擇的key" type="實際對應的ActionForm類"/>
          <form-beans>
          名稱用來在MoudleConfig中的formBeans HashMap中查找相應的FormBeanConfig,而FormBeanConfig
          中有一個type,它保存了上面XML文件中定義的值,用來生成ActionForm。

          通過processPopulate(HttpServletRequest, HttpServletResponse, ActionForm,
          ActionMapping)方法初始化ActionForm 以及 FormBean,這個方法首先會設(shè)定這個ActionForm所屬
          于的ActionServlet,然后對這個ActionForm進行初始化。判斷當前的reqest是否Multipart類型,
          如果是就把相應的MultipartClass類全名保存到request中,key值為Globals.MULTIPART_KEY。調(diào)
          用RequestUtils.populate(...)對request中的參數(shù)進行處理,并保存到相應的FormBean中。在下
          一篇將會對這個方法進行詳細的說明。最后根據(jù)request的parameter判斷當前的請求是否是被取
          消,然后把相關(guān)信息保存到request中:
          if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
          (request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
          request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
          }

          六、下面說明這個request是如何真正被處理的,下面的方法都是在RequestProcessor中定義。

          通過processValidate(HttpServletRequest, HttpServletResponse, ActionForm,
          ActionMapping)判斷request中的parameter是否合法。如果合法就返回true,否則取消這次請求,
          并且返回到指定的輸入頁面。它判斷合法的過程是:如果這次的請求被取消,那么直接返回true;
          如果沒有要求對request中的parameter進行合法性檢驗,也直接返回true;最后通過調(diào)用
          form.validate(mapping, request)執(zhí)行檢驗,如果返回的ActionErrors為null,那么代表通過
          檢驗,返回true,否則取消這次request。取消的過程是:如果這個請求是Multipart類型的,那么
          要對這個MultipartRequestHandler進行回滾;而后,獲取當前的ActionMapping的input值,即在
          config XML 中<action.../>定義的input屬性。如果ActionMapping沒有這個input值,那么調(diào)用
          response.sendError(...)方法,然后返回false;如果存在,就保存驗證出錯信息到request中,
          key為Globals.ERROR_KEY,跳轉(zhuǎn)到input所指定的地址中。

          processForward(HttpServletRequest, HttpServletResponse, ActionMapping)以及
          processInclude(HttpServletRequest, HttpServletResponse, ActionMapping)分別通過執(zhí)行
          RequestDispatcher.forward(request, response) 以及 RequestDispatcher.include(request,
          response)實現(xiàn)對頁面的跳轉(zhuǎn)。

          但是如果當前的請求還有其它操作要執(zhí)行,那么ActionMapping中的include或者forward屬性值就
          會為空。這時需要通過調(diào)用processActionCreate(HttpServletRequest, HttpServletResponse,
          ActionMapping)方法根據(jù)config XML文件的配置信息生成一個Action類,然后通過
          processActionPerform(...)調(diào)用生成的Action中的execute(...)方法。最后根據(jù)execute(...)
          方法返回的ActionForword類,執(zhí)行跳轉(zhuǎn)。在整個過程中有一個url的計算方法,這個將在下一篇
          中說明。


          七、對ActionMaping結(jié)構(gòu)的說明:
          在ActionConfig為保存Moudle Action屬性的一個javabean,它有以下屬性:
          * boolean configured 這個對象的所有屬性是否已經(jīng)被配置完。如果已經(jīng)完成,那么在改變其中
          任何屬性都會拋出IllegalStateException("Configuration is frozen")
          * ModuleConfig moduleConfig 本ActionConfig類所屬于的ModuleConfig。
          * String attribute request范圍 或者 session范圍 的屬性名稱,我們將通過這個屬性名稱來
          獲取相應的form bean,注意,這個屬性與form bean中定義的名稱是不一樣的。注意以下的方
          法:
          public String getAttribute() {
          if (this.attribute == null) {
          return (this.name);
          } else {
          return (this.attribute);
          }
          }
          * String forward 調(diào)用RequestDispatcher.forward()方法時所需要的上下文相關(guān)的地址。
          * String include 調(diào)用RequestDispatcher.include()方法時所需要的上下文相關(guān)的地址。
          * String type Action類的類全名,如果上面的兩個屬性都沒有定義的話,就會用它來處理
          request。
          * String input 如果輸入數(shù)據(jù)沒有通過驗證,將會以這個值為地址,跳轉(zhuǎn)到相應的頁面。
          * String multipartClass MultipartRequestHandler實現(xiàn)類的全名
          * String name 與本類相關(guān)的 form bean 的名稱。
          * String parameter 用于struts的擴展,存儲其它的配置信息。struts自己不會用到這個屬性。
          * String path 上下文相關(guān)的地址,這個地址為下一個將會進入的地址,這個地址必須要以"/"
          開頭。如果使用了extension mapping的話,這個地址將不會帶有擴展名。
          * String roles 一個以","分隔的角色名稱。這些角色將會有權(quán)限訪問這個request。
          * String scope 缺省為session。
          * String prefix,String suffix后綴和前綴。
          * boolean unknown 標志當前的ActionConfig類是否是為了未知的request path而準備的,
          ActionMappings將會根據(jù)這個標志返回這個ActionConfig。
          * boolean validate 是否調(diào)用validate()方法進行數(shù)據(jù)校驗。

          ActionMapping 繼承自 ActionConfig,它增加了從ModuleConfig查找ActionForword的方法。同
          時它還提供了從ModuleConfig查找ExceptionConfig的方法,如果找不到,會通過
          type.getSuperclass()方法,根據(jù)父類的類名稱繼續(xù)查找,直到最后。

          八、關(guān)于request.getServletPath()的解釋:
          request.getServletPath() 的返回值就是在下面url中定義的值,比如如果按照下面的定義
          <url-pattern>
          *.do
          </url-pattern>
          那么:
          http://localhost:8080/servlet/go.do ---------->/go.do
          http://localhost:8080/servlet/where/go.do ---------->/where/go.do
          這里有一個小常識,如果
          <url-pattern>
          /where/*.do
          </url-pattern>
          那么地址中只有http://localhost:8080/servlet/where/*.do有效(tomcat 4.0)


          在ActionConfig為保存Moudle Action屬性的一個javabean,它有以下屬性:
          * boolean configured 這個對象的所有屬性是否已經(jīng)被配置完。如果已經(jīng)完成,那么在改變其中
          任何屬性都會拋出IllegalStateException("Configuration is frozen")
          * ModuleConfig moduleConfig 本ActionConfig類所屬于的ModuleConfig。
          * String attribute request范圍 或者 session范圍 的屬性名稱,我們將通過這個屬性名稱來
          獲取相應的form bean,注意,這個屬性與form bean中定義的名稱是不一樣的。注意以下的方
          法:
          public String getAttribute() {
          if (this.attribute == null) {
          return (this.name);
          } else {
          return (this.attribute);
          }
          }
          * String forward 調(diào)用RequestDispatcher.forward()方法時所需要的上下文相關(guān)的地址。
          * String include 調(diào)用RequestDispatcher.include()方法時所需要的上下文相關(guān)的地址。
          * String type Action類的類全名,如果上面的兩個屬性都沒有定義的話,就會用它來處理
          request。
          * String input
          * String multipartClass MultipartRequestHandler實現(xiàn)類的全名
          * String name 與本類相關(guān)的 form bean 的名稱。
          * String parameter 用于struts的擴展,存儲其它的配置信息。struts自己不會用到這個屬性。
          * String path 上下文相關(guān)的地址,這個地址為下一個將會進入的地址,這個地址必須要以"/"
          開頭。如果使用了extension mapping的話,這個地址將不會帶有擴展名。
          * String roles 一個以","分隔的角色名稱。這些角色將會有權(quán)限訪問這個request。
          * String scope 缺省為session。
          * String prefix,String suffix后綴和前綴。
          * boolean unknown 標志當前的ActionConfig類是否是為了未知的request path而準備的,
          ActionMappings將會根據(jù)這個標志返回這個ActionConfig。
          * boolean validate 是否調(diào)用validate()方法進行數(shù)據(jù)校驗。

          ActionMapping 繼承自 ActionConfig,它增加了從ModuleConfig查找ActionForword的方法。同
          時它還提供了從ModuleConfig查找ExceptionConfig的方法,如果找不到,會通過
          type.getSuperclass()方法,根據(jù)父類的類名稱繼續(xù)查找,直到最后。

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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 巴马| 随州市| 商河县| 岳阳县| 铜山县| 天柱县| 延边| 胶南市| 竹溪县| 腾冲县| 周至县| 嘉义县| 区。| 德江县| 达孜县| 怀化市| 潢川县| 铁岭市| 张家川| 攀枝花市| 安顺市| 长沙市| 铁岭县| 盐源县| 浦江县| 曲水县| 肥城市| 兰坪| 宁明县| 定远县| 洱源县| 襄汾县| 光山县| 米林县| 龙泉市| 婺源县| 南部县| 资中县| 旬阳县| 彭州市| 雷山县|