Request是如何被處理的
ActionServlet是Struts框架中唯一的Servlet,它負責處理所有request。無論何時接收到一個request,它都會先嘗試為當前的request尋找一個sub-application。一旦一個sub-application被找到,ActionServlet就會為那個sub-application創建一個RequestProcessor對象,調用這個對象的process()方法并把HttpServletRequest和HttpServletResponse對象傳入。
RequestProcessor.process()就是大部分request被處理的地方。process()方法使用了Template Method模式實現,其中有很多獨立的方法來執行請求處理的每一步驟,這些方法將會在process方法中依次被調用。比如,將會有一個獨立的方法用來尋找當前request對應的ActionForm類,一個方法來檢查當前用戶是否有執行action mapping所必須的權限。這些給與我們極大的靈活性。在發布的Struts包中有一個RequestProcessor類提供了請求處理每一步驟的默認實現。這就意味著你可以僅僅重寫你感興趣的方法,其它的使用默認的實現。舉例來說,默認地Struts調用request.isUserInRole()來檢查用戶是否有權限執行當前的ActionMapping;這時如果你想通過查詢數據庫來實現,你所要做的就是重寫processRoles()方法,通過查詢出的用戶是否擁有必須的權限來返回true或false。
首先我們將會看到缺省情況下,process()方法是如何實現的,然后我將會詳細解釋默認的RequestProcessor類中的每一個方法,這樣你就可以決定哪一部分是你想要改變的。
public void process(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException { // Wrap multipart requests with a special wrapper request = processMultipart(request); // Identify the path component we will // use to select a mapping String path = processPath(request, response); if (path == null) { return; } if (log.isDebugEnabled()) { log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'"); } // Select a Locale for the current user if requested processLocale(request, response); // Set the content type and no-caching headers // if requested processContent(request, response); processNoCache(request, response); // General purpose preprocessing hook if (!processPreprocess(request, response)) { return; } // Identify the mapping for this request ActionMapping mapping = processMapping(request, response, path); if (mapping == null) { return; } // Check for any role required to perform this action if (!processRoles(request, response, mapping)) { return; } // Process any ActionForm bean related to this request ActionForm form = processActionForm(request, response, mapping); processPopulate(request, response, form, mapping); if (!processValidate(request, response, form, mapping)) { return; } // Process a forward or include specified by this mapping if (!processForward(request, response, mapping)) { return; } if (!processInclude(request, response, mapping)) { return; } // Create or acquire the Action instance to // process this request Action action = processActionCreate(request, response, mapping); if (action == null) { return; } // Call the Action instance itself ActionForward forward = processActionPerform(request, response,action, form, mapping); // Process the returned ActionForward instance processForwardConfig(request, response, forward); } |
1、processMutipart():在這個方法中,Struts將會讀取request來檢查request的contentType是否是multipart/form-data。如果是的話,將會解析request并且將之包裝到HttpServletRequest中。當你創建了一個HTML FORM用來提交數據,那么request的contentType默認就是application/x-www-form-urlencoded。但是如果你的form使用了file類型的input控件允許用戶上傳文件的話,你就必須將contentType改為multipart/form-data。如果是這樣的情況,你就不能再通過getParameter()來獲取用戶提交的數據;你必須將request作為一個InputStream來讀取,并且自己解析它來獲得參數值。
2、processPath():在這個方法中,Struts將會讀取request的URI,來確定路徑元素,這個元素是用來獲取ActionMappint元素。
3、processLocale():在這個方法中,Struts將會為當前request取得Locale,如果配置過的話,還可以將這個對象作為HttpSession中org.apache.struts.action.LOCALE屬性的值而保存。作為這個方法的副作用,HttpSession將會被創建,如果你不想創建的話,你可以在ControllerConfig中將locale屬性設為false,在struts-config.xml中象如下這樣:
<controller> <set-property property="locale" value="false"/> </controller> |
4、processContent():通過調用response.setContentType()來為response設置contentType。這個方法首先會嘗試從struts-config.xml配置中得到contentType。缺省情況下使用text/html。如果想覆蓋它,可以象如下這樣:
<controller> <set-property property="contentType" value="text/plain"/> </controller> |
5、processNoCache():如果配置是no-cache,Struts將會為每個response設置下面三個headers:
requested in struts config.xml response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 1); |
如果你想設置no-cache header,在struts-config.xml中加入下面信息:
<controller> <set-property property="noCache" value="true"/> </controller> |
6、processPreprocess():這個方法為預處理提供一個hook,可以在子類中覆蓋它。它的缺省實現沒有作任何事情,總是返回true。返回false的話將會終止當前請求的處理。
7、processMapping():這個方法將會用路徑信息得到一個ActionMapping對象。也就是struts-config.xml文件中的<action>元素:
<action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request"> <forward name="sucess" path="/sucessPage.do"/> <forward name="failure" path="/failurePage.do"/> </action> |
ActionMapping元素包含了Action類的名稱和處理請求使用的ActionForm等等信息。它還包含當前ActionMapping配置的ActionForwards信息。
8、processRoles():Struts web應用提供了一個授權方案。也就是說,一旦一個用戶登入了容器,struts的processRoles()方法將會通過調用request.isUserInRole(),來檢查他是否有必須的角色來運行一個給定的ActionMapping。
<action path="/addUser" roles="administrator"/> |
假設你有一個AddUserAction并且你只想讓administrator能夠增加新的user。你所要做的就是給你的AddUserAction元素增加一個role屬性,這個屬性的值為administrator。這樣,在運行AddUserAction之前,這個方法會確保用戶擁有administraotr的角色。
9、processActionForm():每一個ActionMapping都一個相應的ActionForm類。當Struts處理一個ActionMapping的時候,它將會從<action>元素的name屬性中找出對應的ActionForm類的名稱。
<form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm"> ?。糵orm-property name="firstName" type="java.lang.String"/> ?。糵orm-property name="lastName" type="java.lang.String"/> </form-bean> |
在我們的例子中,它會先在request scope中檢查是否有一個org.apache.struts.action.DynaActionForm類的對象存在。如果有它將會使用這個對象,如果沒有它將會創建一個新的對象并把它設置在request scope。
10、processPopulate():在這個方法中,Struts將會用相匹配的request參數裝配ActionForm的實例變量。
11、processValidate():Struts將會調用你的ActionForm類的validate方法。如果你從validate()返回ActionErrors,它將會將user重定向到<action>元素的input屬性指定的頁面。
12、processForward()和processInclude():在這些方法中,Struts將會檢查<action>元素的forward或include屬性,如果找到了,將會把forward或include請求放置到配置的頁面中。
<action forward="/Login.jsp" path="/loginInput"/> <action include="/Login.jsp" path="/loginInput"/> |
你可以從這些方法的名字上猜測它們的不同:processForward()最終調用RequestDispatcher.forward(),而processInclude()調用RequestDispatcher.include()。如果你同時配置了forward和include屬性,它將會總是調用forward,因為forward先被處理。
13、processActionCreate():這個方法從<action>元素的type屬性中獲取獲得Action類的名字并且創建返回它的實例。在我們的例子中,它將會創建一個com.sample.NewContactAction類的實例。
14、processActionPerform():這個方法調用你的Action類的excute()方法,你的業務邏輯也就是在excute方法中。
15、processForwardConfig():你的Action類的excute()方法將會返回一個ActionForward對象,這個對象將指出哪個頁面是顯示給用戶的頁面。因此,Struts將會為那個頁面創建一個RequestDispatcher,并且調用RequestDispatcher.forward()。
上面的列表說明了默認的RequestProcessor實現在處理請求時每一步作的工作,以及執行的順序。正如你所看到的,RequestProcessor是非常靈活的,允許你通過設置<controller>元素的屬性來配置它。舉例來說,如果你的應用準備生成XML內容來代替HTML,你就可以通過設置controller元素的屬性來通知Struts這些情況。