第二篇
struts的執(zhí)行(
struts-1.1版)
本篇詳細介紹
struts在初始化之后是如何處理一個請求,并返回數(shù)據(jù)的。這里最核心的類是
requestprocessor以及requestutils。requestprocessor類通過requestdispatcher實現(xiàn)頁面的跳轉(zhuǎn),
而requestprocessor負(fù)責(zé)處理request中傳來的請求信息,存放到formbeanconfig中,以及對要跳轉(zhuǎn)的
url進行處理。
struts 在初始化完成之后,會根據(jù)請求調(diào)用doget(...)或者dopost(...)方法,這兩個方法直接
調(diào)用process(request, response)方法。process(...)方法首先判斷當(dāng)前的request屬于
哪一個moduleconfig,然后生成與這個moduleconifg相對應(yīng)的requestprocessor,最后調(diào)用這個
requestprocessor的process(...)方法,執(zhí)行request的請求。
一、requestutils.selectmodule(string prefix, http
servletrequest,
servletcontext)方法:
這個方法,根據(jù)prefix,從
servletcontext中選擇相應(yīng)的moduleconfig,然后把這個moduleconfig
保存到request中。
servletcontext對應(yīng)的key值為globals.module_key + prefix,保存到request
中使用的key值為globals.module_key。如果在
servletcontext中不存在這樣的一個moduleconfig,
那么調(diào)用request.removeattribute(globals.module_key)方法。然后以同樣的方法查找、保存
messageresources對象。當(dāng)prefix為空時,會調(diào)用下面的方法選擇moduleconfig。
二、requestutils.selectmodule(http
servletrequest,
servletcontext)
這個方發(fā)首先使用getmodulename(http
servletrequest,
servletcontext)獲取相應(yīng)的path,然后
通過調(diào)用getmodulename(string matchpath,
servletcontext)獲取相應(yīng)的prefix。以這prefix為
參數(shù)調(diào)用(一)中的selectmodule(...)選擇moduleconfig。
獲取path的過程為:首先從request中查找名稱為
include_
servlet_path(
javax.
servlet.include.
servlet_path)的屬性,如果為空,就調(diào)用
request.get
servletpath()方法獲取
servletpath。
獲取prefix的過程為:它首先調(diào)用getmoduleprefixes(
servletcontext),獲取所有已存在的
module前綴。 然后通過分析上面獲取的path來判斷當(dāng)前的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(http
servletrequest)
這個方法就是獲取上面的selectmodule(...)方法所得到的moduleconfig。如果找不到這樣的
moduleconfig,那么就把
servletcontext中缺省的moduleconfig返回(調(diào)用
get
servletcontext().getattribute(globals.module_key))
四、getrequestprocessor(moduleconfig config)
這個方法從根據(jù)moduleconfig的prefix作為key,從
servletcontext中獲取requestprocessor。這
個key值為globals.request_processor_key + config.getprefix()。
如果
servletcontext中不存在這樣的一個requestprocessor,那么就生成一個新的
requestprocessor的實例,完成初始化(保存action
servlet以及moduleconfig到這個新的實例中)后
將其保存到
servletcontext中。
五、requestprocessor.process(http
servletrequest, http
servletresponse)
這是真正執(zhí)行http
servletrequst請求的方法。
這個方法首先判斷但前的http
servletrequest是否是multipart類型的request,也就是說當(dāng)前
的request是否有字段是"file"類型。這樣做的原因是這種類型的request中g(shù)etparameter()等
相類似的方法都是無法執(zhí)行的,所以要區(qū)分對待。如果是multipart類型的,那么把這個request包
裝成multipartrequestwrapper類。
multipartrequestwrapper 與 http
servletrequest不同的就是重新實現(xiàn)了
setparameter(string name, string value),getparameter(string name),getparameternames()
以及getparametervalues(string name)方法。
這些方法的思想是所有的變量名、值對都保存到一個hashmap里:key為變量名,value為變量值,但
是注意value都是string[]格式的。當(dāng)有一個新的值加入的時候,通過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(http
servletrequest, http
servletresponse)獲取地址,以這個地址作為
從moduleconfig獲取actionmapping的key。這里首先查詢
request.getattribute(include_path_info)中是否有這樣的一個值,如果為空就調(diào)用
request.getpathinfo()方法獲取path。如果這樣還是獲取不了,就從
request.getattribute(include_
servlet_path)方法中獲取path,找不到就使用
request.get
servletpath()得到的path進行分析。分析過程如下:
然后如果這個path不是屬于當(dāng)前的moduleconfig的話,直接返回null。截取prefix后面的字符串,
如果這個字符串以.xx結(jié)尾,那么就截取"."前面的字符,比如
http://localhost:8080/
servlet/where/go.do。where為module的名稱(prefix),那么我們獲取的
path值為/go。
然后是通過processlocale(http
servletrequest ,http
servletresponse)為當(dāng)前用戶設(shè)定一個local
對象,它查找的順序是:moduleconfig.getcontrollerconfig().getlocale(),
session.getattribute(globals.locale_key),request.getlocale()。你可以在config
xml文件
中通過<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。
調(diào)用processcontent(http
servletrequest, http
servletresponse)為response設(shè)定contenttype。
這個contenttype是從moduleconfig.getcontrollerconfig().getcontenttype()獲取的。你可以
在config
xml文件中通過<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。
通過processnocache(http
servletrequest, http
servletresponse)方法設(shè)置response的緩存。
你可以在config
xml文件中通過<controller><set-property..../></controller>執(zhí)行相關(guān)的定義。
processpreprocess(http
servletrequest, http
servletresponse)預(yù)處理request,這個方法是預(yù)
留的,用戶可以根據(jù)自己的需要加一些預(yù)處理的程序。
通過processmapping(http
servletrequest, http
servletresponse, string path)以
processpath(...)的返回值為key從moduleconfig中查找actionmapping 對象。如果找到了,那么保
存這個mapping到request中,key值為globals.mapping_key。如果不存在,那么遍歷moduleconfig
中所有的actionmapping,查找哪一個是缺省的actionmapping。然后把它保存到request中,key值
為globals.mapping_key。
通過processroles(http
servletrequest,http
servletresponse,actionmapping)檢查當(dāng)前用戶是
否有權(quán)限執(zhí)行這個請求。如果request.isuserinrole(roles[i])返回true,則代表有。
通過processactionform(http
servletrequest, http
servletresponse, 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="實際對應(yīng)的actionform類"/>
<form-beans>
名稱用來在moudleconfig中的formbeans hashmap中查找相應(yīng)的formbeanconfig,而formbeanconfig
中有一個type,它保存了上面
xml文件中定義的值,用來生成actionform。
通過processpopulate(http
servletrequest, http
servletresponse, actionform,
actionmapping)方法初始化actionform 以及 formbean,這個方法首先會設(shè)定這個actionform所屬
于的action
servlet,然后對這個actionform進行初始化。判斷當(dāng)前的reqest是否multipart類型,
如果是就把相應(yīng)的multipartclass類全名保存到request中,key值為globals.multipart_key。調(diào)
用requestutils.populate(...)對request中的參數(shù)進行處理,并保存到相應(yīng)的formbean中。在下
一篇將會對這個方法進行詳細的說明。最后根據(jù)request的parameter判斷當(dāng)前的請求是否是被取
消,然后把相關(guān)信息保存到request中:
if ((request.getparameter(const
ants.cancel_property) != null)
(request.getparameter(const
ants.cancel_property_x) != null)) {
request.setattribute(globals.cancel_key, boolean.true);
}
六、下面說明這個request是如何真正被處理的,下面的方法都是在requestprocessor中定義。
通過processvalidate(http
servletrequest, http
servletresponse, actionform,
actionmapping)判斷request中的parameter是否合法。如果合法就返回true,否則取消這次請求,
并且返回到指定的輸入頁面。它判斷合法的過程是:如果這次的請求被取消,那么直接返回true;
如果沒有要求對request中的parameter進行合法性檢驗,也直接返回true;最后通過調(diào)用
form.validate(mapping, request)執(zhí)行檢驗,如果返回的actionerrors為null,那么代表通過
檢驗,返回true,否則取消這次request。取消的過程是:如果這個請求是multipart類型的,那么
要對這個multipartrequesthandler進行回滾;而后,獲取當(dāng)前的actionmapping的input值,即在
config
xml 中<action.../>定義的input屬性。如果actionmapping沒有這個input值,那么調(diào)用
response.senderror(...)方法,然后返回false;如果存在,就保存驗證出錯信息到request中,
key為globals.error_key,跳轉(zhuǎn)到input所指定的地址中。
processforward(http
servletrequest, http
servletresponse, actionmapping)以及
processinclude(http
servletrequest, http
servletresponse, actionmapping)分別通過執(zhí)行
requestdispatcher.forward(request, response) 以及 requestdispatcher.include(request,
response)實現(xiàn)對頁面的跳轉(zhuǎn)。
但是如果當(dāng)前的請求還有其它操作要執(zhí)行,那么actionmapping中的include或者forward屬性值就
會為空。這時需要通過調(diào)用processactioncreate(http
servletrequest, http
servletresponse,
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范圍 的屬性名稱,我們將通過這個屬性名稱來
獲取相應(yīng)的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)到相應(yīng)的頁面。
* 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 標(biāo)志當(dāng)前的actionconfig類是否是為了未知的request path而準(zhǔn)備的,
actionmappings將會根據(jù)這個標(biāo)志返回這個actionconfig。
* boolean validate 是否調(diào)用validate()方法進行數(shù)據(jù)校驗。
actionmapping 繼承自 actionconfig,它增加了從moduleconfig查找actionforword的方法。同
時它還提供了從moduleconfig查找exceptionconfig的方法,如果找不到,會通過
type.getsuperclass()方法,根據(jù)父類的類名稱繼續(xù)查找,直到最后。
八、關(guān)于request.get
servletpath()的解釋:
request.get
servletpath() 的返回值就是在下面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范圍 的屬性名稱,我們將通過這個屬性名稱來
獲取相應(yīng)的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 標(biāo)志當(dāng)前的actionconfig類是否是為了未知的request path而準(zhǔn)備的,
actionmappings將會根據(jù)這個標(biāo)志返回這個actionconfig。
* boolean validate 是否調(diào)用validate()方法進行數(shù)據(jù)校驗。
actionmapping 繼承自 actionconfig,它增加了從moduleconfig查找actionforword的方法。同
時它還提供了從moduleconfig查找exceptionconfig的方法,如果找不到,會通過
type.getsuperclass()方法,根據(jù)父類的類名稱繼續(xù)查找,直到最后。