Posted on 2005-08-14 17:48
風(fēng)太少 閱讀(355)
評(píng)論(0) 編輯 收藏
這么久才些好第二篇,說(shuō)明我是一個(gè)很懶惰的人:)。第一篇文章雖然由很多人看,但是得到的反饋比較少,所以也不知道對(duì)大家有沒(méi)有用。同樣的,我的email為:mariah_fan@hotmail.com,希望大家多提意見(jiàn)。
第二篇 struts的執(zhí)行(struts-1.1版)
本篇詳細(xì)介紹struts在初始化之后是如何處理一個(gè)請(qǐng)求,并返回?cái)?shù)據(jù)的。這里最核心的類是
RequestProcessor以及RequestUtils。RequestProcessor類通過(guò)RequestDispatcher實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn),
而RequestProcessor負(fù)責(zé)處理request中傳來(lái)的請(qǐng)求信息,存放到FormBeanConfig中,以及對(duì)要跳轉(zhuǎn)的
url進(jìn)行處理。
struts 在初始化完成之后,會(huì)根據(jù)請(qǐng)求調(diào)用doGet(...)或者doPost(...)方法,這兩個(gè)方法直接
調(diào)用process(request, response)方法。process(...)方法首先判斷當(dāng)前的request屬于
哪一個(gè)ModuleConfig,然后生成與這個(gè)ModuleConifg相對(duì)應(yīng)的RequestProcessor,最后調(diào)用這個(gè)
RequestProcessor的process(...)方法,執(zhí)行request的請(qǐng)求。
一、RequestUtils.selectModule(String prefix, HttpServletRequest,ServletContext)方法:
這個(gè)方法,根據(jù)prefix,從ServletContext中選擇相應(yīng)的ModuleConfig,然后把這個(gè)ModuleConfig
保存到request中。ServletContext對(duì)應(yīng)的key值為Globals.MODULE_KEY + prefix,保存到request
中使用的key值為Globals.MODULE_KEY。如果在ServletContext中不存在這樣的一個(gè)ModuleConfig,
那么調(diào)用request.removeAttribute(Globals.MODULE_KEY)方法。然后以同樣的方法查找、保存
MessageResources對(duì)象。當(dāng)prefix為空時(shí),會(huì)調(diào)用下面的方法選擇ModuleConfig。
二、RequestUtils.selectModule(HttpServletRequest, ServletContext)
這個(gè)方發(fā)首先使用getModuleName(HttpServletRequest, ServletContext)獲取相應(yīng)的path,然后
通過(guò)調(diào)用getModuleName(String matchPath, ServletContext)獲取相應(yīng)的prefix。以這prefix為
參數(shù)調(diào)用(一)中的selectModule(...)選擇ModuleConfig。
獲取path的過(guò)程為:首先從request中查找名稱為
INCLUDE_SERVLET_PATH(javax.servlet.include.servlet_path)的屬性,如果為空,就調(diào)用
request.getServletPath()方法獲取servletPath。
獲取prefix的過(guò)程為:它首先調(diào)用getModulePrefixes(ServletContext),獲取所有已存在的
module前綴。 然后通過(guò)分析上面獲取的path來(lái)判斷當(dāng)前的url屬于哪一個(gè)module,方法是截取
path中最后一個(gè)"/"字符前面的字符串,然后與有上面方法中獲取的prefixes[]逐個(gè)對(duì)比,如果
prefixes[]中存在這樣的一個(gè)值,則返回這個(gè)截取的字符串,否則繼續(xù)截取path最后面的"/"前面
的字符串,然后對(duì)比。如果始終找不到,則返回""。
getModulePrefixes(ServletContext)的執(zhí)行過(guò)程是:首先通過(guò)
context.getAttribute(PREFIXES_KEY)查找是否存在這樣的一個(gè)保存所有的prefix的string array,
如果存在,就說(shuō)明已經(jīng)解析過(guò)一次了,就直接返回這個(gè)string array,否則遍歷ServletContext中
所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)開(kāi)頭的
atrribute name,然后截取這個(gè)atrribute name中MODULE_KEY后面的字符串,作為一個(gè)prefix. 最
后把通過(guò)這個(gè)這個(gè)方式獲取的所有的prefix作為一個(gè)ArrayList存儲(chǔ)到ServletContext中,屬性key
值為PREFIXES_KEY(org.apache.struts.util.PREFIXES),這樣下次再次查找的時(shí)候就可以直接從這
個(gè)attribute中獲取了。
三、getModuleConfig(HttpServletRequest)
這個(gè)方法就是獲取上面的selectModule(...)方法所得到的ModuleConfig。如果找不到這樣的
ModuleConfig,那么就把ServletContext中缺省的ModuleConfig返回(調(diào)用
getServletContext().getAttribute(Globals.MODULE_KEY))
四、getRequestProcessor(ModuleConfig config)
這個(gè)方法從根據(jù)ModuleConfig的prefix作為key,從ServletContext中獲取RequestProcessor。這
個(gè)key值為Globals.REQUEST_PROCESSOR_KEY + config.getPrefix()。
如果ServletContext中不存在這樣的一個(gè)RequestProcessor,那么就生成一個(gè)新的
RequestProcessor的實(shí)例,完成初始化(保存ActionServlet以及ModuleConfig到這個(gè)新的實(shí)例中)后
將其保存到ServletContext中。
五、RequestProcessor.process(HttpServletRequest, HttpServletResponse)
這是真正執(zhí)行HttpServletRequst請(qǐng)求的方法。
這個(gè)方法首先判斷但前的HttpServletRequest是否是Multipart類型的request,也就是說(shuō)當(dāng)前
的request是否有字段是"file"類型。這樣做的原因是這種類型的request中g(shù)etParameter()等
相類似的方法都是無(wú)法執(zhí)行的,所以要區(qū)分對(duì)待。如果是Multipart類型的,那么把這個(gè)request包
裝成MultipartRequestWrapper類。
MultipartRequestWrapper 與 HttpServletRequest不同的就是重新實(shí)現(xiàn)了
setParameter(String name, String value),getParameter(String name),getParameterNames()
以及getParameterValues(String name)方法。
這些方法的思想是所有的變量名、值對(duì)都保存到一個(gè)HashMap里:key為變量名,value為變量值,但
是注意value都是String[]格式的。當(dāng)有一個(gè)新的值加入的時(shí)候,通過(guò)setParameter(...)
方法,把值加到數(shù)組的最后。在setParameter(...)中有一個(gè)比較少見(jiàn)的保存值的方法,記錄如下:
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)獲取地址,以這個(gè)地址作為
從ModuleConfig獲取ActionMapping的Key。這里首先查詢
request.getAttribute(INCLUDE_PATH_INFO)中是否有這樣的一個(gè)值,如果為空就調(diào)用
request.getPathInfo()方法獲取path。如果這樣還是獲取不了,就從
request.getAttribute(INCLUDE_SERVLET_PATH)方法中獲取path,找不到就使用
request.getServletPath()得到的path進(jìn)行分析。分析過(guò)程如下:
然后如果這個(gè)path不是屬于當(dāng)前的ModuleConfig的話,直接返回null。截取prefix后面的字符串,
如果這個(gè)字符串以.XX結(jié)尾,那么就截取"."前面的字符,比如
http://localhost:8080/servlet/where/go.do。where為module的名稱(prefix),那么我們獲取的
path值為/go。
然后是通過(guò)processLocale(HttpServletRequest ,HttpServletResponse)為當(dāng)前用戶設(shè)定一個(gè)Local
對(duì)象,它查找的順序是:moduleConfig.getControllerConfig().getLocale(),
session.getAttribute(Globals.LOCALE_KEY),request.getLocale()。你可以在config XML文件
中通過(guò)<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。
調(diào)用processContent(HttpServletRequest, HttpServletResponse)為response設(shè)定contentType。
這個(gè)contentType是從moduleConfig.getControllerConfig().getContentType()獲取的。你可以
在config XML文件中通過(guò)<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。
通過(guò)processNoCache(HttpServletRequest, HttpServletResponse)方法設(shè)置response的緩存。
你可以在config XML文件中通過(guò)<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。
processPreprocess(HttpServletRequest, HttpServletResponse)預(yù)處理request,這個(gè)方法是預(yù)
留的,用戶可以根據(jù)自己的需要加一些預(yù)處理的程序。
通過(guò)processMapping(HttpServletRequest, HttpServletResponse, String path)以
processPath(...)的返回值為key從ModuleConfig中查找ActionMapping 對(duì)象。如果找到了,那么保
存這個(gè)Mapping到request中,key值為Globals.MAPPING_KEY。如果不存在,那么遍歷ModuleConfig
中所有的ActionMapping,查找哪一個(gè)是缺省的ActionMapping。然后把它保存到request中,key值
為Globals.MAPPING_KEY。
通過(guò)processRoles(HttpServletRequest,HttpServletResponse,ActionMapping)檢查當(dāng)前用戶是
否有權(quán)限執(zhí)行這個(gè)請(qǐng)求。如果request.isUserInRole(roles[i])返回true,則代表有。
通過(guò)processActionForm(HttpServletRequest, HttpServletResponse, ActionMapping)生成一個(gè)
ActionForm,然后根據(jù)ActionMapping所屬的scope,保存到request或者session中。key值為這個(gè)
ActionMapping中的attribute屬性。ActionForm是通過(guò)RequestUtils.createActionForm(...)方法
獲取的,在下一篇將會(huì)對(duì)這個(gè)方法進(jìn)行詳細(xì)的說(shuō)明。這里只說(shuō)明ActionForm與FormBeanConfig以及
FormPropertyConfig之間的區(qū)別。每個(gè)FormPropertyConfig代表Form表單的一個(gè)字段,表單中的所
有FormPropertyConfig以一個(gè)HashMap保存到FormBeanConfig中。而FormBeanConfig是在
Actionservet初始化的時(shí)候生成的:
<form-beans>
<form-bean name="一個(gè)名稱,作為action選擇的key" type="實(shí)際對(duì)應(yīng)的ActionForm類"/>
<form-beans>
名稱用來(lái)在MoudleConfig中的formBeans HashMap中查找相應(yīng)的FormBeanConfig,而FormBeanConfig
中有一個(gè)type,它保存了上面XML文件中定義的值,用來(lái)生成ActionForm。
通過(guò)processPopulate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)方法初始化ActionForm 以及 FormBean,這個(gè)方法首先會(huì)設(shè)定這個(gè)ActionForm所屬
于的ActionServlet,然后對(duì)這個(gè)ActionForm進(jìn)行初始化。判斷當(dāng)前的reqest是否Multipart類型,
如果是就把相應(yīng)的MultipartClass類全名保存到request中,key值為Globals.MULTIPART_KEY。調(diào)
用RequestUtils.populate(...)對(duì)request中的參數(shù)進(jìn)行處理,并保存到相應(yīng)的FormBean中。在下
一篇將會(huì)對(duì)這個(gè)方法進(jìn)行詳細(xì)的說(shuō)明。最后根據(jù)request的parameter判斷當(dāng)前的請(qǐng)求是否是被取
消,然后把相關(guān)信息保存到request中:
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
六、下面說(shuō)明這個(gè)request是如何真正被處理的,下面的方法都是在RequestProcessor中定義。
通過(guò)processValidate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)判斷request中的parameter是否合法。如果合法就返回true,否則取消這次請(qǐng)求,
并且返回到指定的輸入頁(yè)面。它判斷合法的過(guò)程是:如果這次的請(qǐng)求被取消,那么直接返回true;
如果沒(méi)有要求對(duì)request中的parameter進(jìn)行合法性檢驗(yàn),也直接返回true;最后通過(guò)調(diào)用
form.validate(mapping, request)執(zhí)行檢驗(yàn),如果返回的ActionErrors為null,那么代表通過(guò)
檢驗(yàn),返回true,否則取消這次request。取消的過(guò)程是:如果這個(gè)請(qǐng)求是Multipart類型的,那么
要對(duì)這個(gè)MultipartRequestHandler進(jìn)行回滾;而后,獲取當(dāng)前的ActionMapping的input值,即在
config XML 中<action.../>定義的input屬性。如果ActionMapping沒(méi)有這個(gè)input值,那么調(diào)用
response.sendError(...)方法,然后返回false;如果存在,就保存驗(yàn)證出錯(cuò)信息到request中,
key為Globals.ERROR_KEY,跳轉(zhuǎn)到input所指定的地址中。
processForward(HttpServletRequest, HttpServletResponse, ActionMapping)以及
processInclude(HttpServletRequest, HttpServletResponse, ActionMapping)分別通過(guò)執(zhí)行
RequestDispatcher.forward(request, response) 以及 RequestDispatcher.include(request,
response)實(shí)現(xiàn)對(duì)頁(yè)面的跳轉(zhuǎn)。
但是如果當(dāng)前的請(qǐng)求還有其它操作要執(zhí)行,那么ActionMapping中的include或者forward屬性值就
會(huì)為空。這時(shí)需要通過(guò)調(diào)用processActionCreate(HttpServletRequest, HttpServletResponse,
ActionMapping)方法根據(jù)config XML文件的配置信息生成一個(gè)Action類,然后通過(guò)
processActionPerform(...)調(diào)用生成的Action中的execute(...)方法。最后根據(jù)execute(...)
方法返回的ActionForword類,執(zhí)行跳轉(zhuǎn)。在整個(gè)過(guò)程中有一個(gè)url的計(jì)算方法,這個(gè)將在下一篇
中說(shuō)明。
七、對(duì)ActionMaping結(jié)構(gòu)的說(shuō)明:
在ActionConfig為保存Moudle Action屬性的一個(gè)javabean,它有以下屬性:
* boolean configured 這個(gè)對(duì)象的所有屬性是否已經(jīng)被配置完。如果已經(jīng)完成,那么在改變其中
任何屬性都會(huì)拋出IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig類所屬于的ModuleConfig。
* String attribute request范圍 或者 session范圍 的屬性名稱,我們將通過(guò)這個(gè)屬性名稱來(lái)
獲取相應(yīng)的form bean,注意,這個(gè)屬性與form bean中定義的名稱是不一樣的。注意以下的方
法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 調(diào)用RequestDispatcher.forward()方法時(shí)所需要的上下文相關(guān)的地址。
* String include 調(diào)用RequestDispatcher.include()方法時(shí)所需要的上下文相關(guān)的地址。
* String type Action類的類全名,如果上面的兩個(gè)屬性都沒(méi)有定義的話,就會(huì)用它來(lái)處理
request。
* String input 如果輸入數(shù)據(jù)沒(méi)有通過(guò)驗(yàn)證,將會(huì)以這個(gè)值為地址,跳轉(zhuǎn)到相應(yīng)的頁(yè)面。
* String multipartClass MultipartRequestHandler實(shí)現(xiàn)類的全名
* String name 與本類相關(guān)的 form bean 的名稱。
* String parameter 用于struts的擴(kuò)展,存儲(chǔ)其它的配置信息。struts自己不會(huì)用到這個(gè)屬性。
* String path 上下文相關(guān)的地址,這個(gè)地址為下一個(gè)將會(huì)進(jìn)入的地址,這個(gè)地址必須要以"/"
開(kāi)頭。如果使用了extension mapping的話,這個(gè)地址將不會(huì)帶有擴(kuò)展名。
* String roles 一個(gè)以","分隔的角色名稱。這些角色將會(huì)有權(quán)限訪問(wèn)這個(gè)request。
* String scope 缺省為session。
* String prefix,String suffix后綴和前綴。
* boolean unknown 標(biāo)志當(dāng)前的ActionConfig類是否是為了未知的request path而準(zhǔn)備的,
ActionMappings將會(huì)根據(jù)這個(gè)標(biāo)志返回這個(gè)ActionConfig。
* boolean validate 是否調(diào)用validate()方法進(jìn)行數(shù)據(jù)校驗(yàn)。
ActionMapping 繼承自 ActionConfig,它增加了從ModuleConfig查找ActionForword的方法。同
時(shí)它還提供了從ModuleConfig查找ExceptionConfig的方法,如果找不到,會(huì)通過(guò)
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
這里有一個(gè)小常識(shí),如果
<url-pattern>
/where/*.do
</url-pattern>
那么地址中只有http://localhost:8080/servlet/where/*.do有效(tomcat 4.0)
在ActionConfig為保存Moudle Action屬性的一個(gè)javabean,它有以下屬性:
* boolean configured 這個(gè)對(duì)象的所有屬性是否已經(jīng)被配置完。如果已經(jīng)完成,那么在改變其中
任何屬性都會(huì)拋出IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig類所屬于的ModuleConfig。
* String attribute request范圍 或者 session范圍 的屬性名稱,我們將通過(guò)這個(gè)屬性名稱來(lái)
獲取相應(yīng)的form bean,注意,這個(gè)屬性與form bean中定義的名稱是不一樣的。注意以下的方
法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 調(diào)用RequestDispatcher.forward()方法時(shí)所需要的上下文相關(guān)的地址。
* String include 調(diào)用RequestDispatcher.include()方法時(shí)所需要的上下文相關(guān)的地址。
* String type Action類的類全名,如果上面的兩個(gè)屬性都沒(méi)有定義的話,就會(huì)用它來(lái)處理
request。
* String input
* String multipartClass MultipartRequestHandler實(shí)現(xiàn)類的全名
* String name 與本類相關(guān)的 form bean 的名稱。
* String parameter 用于struts的擴(kuò)展,存儲(chǔ)其它的配置信息。struts自己不會(huì)用到這個(gè)屬性。
* String path 上下文相關(guān)的地址,這個(gè)地址為下一個(gè)將會(huì)進(jìn)入的地址,這個(gè)地址必須要以"/"
開(kāi)頭。如果使用了extension mapping的話,這個(gè)地址將不會(huì)帶有擴(kuò)展名。
* String roles 一個(gè)以","分隔的角色名稱。這些角色將會(huì)有權(quán)限訪問(wèn)這個(gè)request。
* String scope 缺省為session。
* String prefix,String suffix后綴和前綴。
* boolean unknown 標(biāo)志當(dāng)前的ActionConfig類是否是為了未知的request path而準(zhǔn)備的,
ActionMappings將會(huì)根據(jù)這個(gè)標(biāo)志返回這個(gè)ActionConfig。
* boolean validate 是否調(diào)用validate()方法進(jìn)行數(shù)據(jù)校驗(yàn)。
ActionMapping 繼承自 ActionConfig,它增加了從ModuleConfig查找ActionForword的方法。同
時(shí)它還提供了從ModuleConfig查找ExceptionConfig的方法,如果找不到,會(huì)通過(guò)
type.getSuperclass()方法,根據(jù)父類的類名稱繼續(xù)查找,直到最后。