第一篇:詞法分析設(shè)計(jì)實(shí)驗(yàn)報(bào)告(附代碼)
實(shí)驗(yàn)一
詞法分析設(shè)計(jì)
實(shí)驗(yàn)學(xué)時(shí):4 實(shí)驗(yàn)類型:綜合 實(shí)驗(yàn)要求:必修
一、實(shí)驗(yàn)?zāi)康?/p>
通過本實(shí)驗(yàn)的編程實(shí)踐,使學(xué)生了解詞法分析的任務(wù),掌握詞法分析程序設(shè)計(jì)的原理和構(gòu)造方法,使學(xué)生對(duì)編譯的基本概念、原理和方法有完整的和清楚的理解,并能正確地、熟練地運(yùn)用。
二、實(shí)驗(yàn)內(nèi)容
用VC++/VB/JAVA語言實(shí)現(xiàn)對(duì)C語言子集的源程序進(jìn)行詞法分析。通過輸入源程序從左到右對(duì)字符串進(jìn)行掃描和分解,依次輸出各個(gè)單詞的內(nèi)部編碼及單詞符號(hào)自身值;若遇到錯(cuò)誤則顯示“Error”,然后跳過錯(cuò)誤部分繼續(xù)顯示 ;同時(shí)進(jìn)行標(biāo)識(shí)符登記符號(hào)表的管理。以下是實(shí)現(xiàn)詞法分析設(shè)計(jì)的主要工作:(1)從源程序文件中讀入字符。
(2)統(tǒng)計(jì)行數(shù)和列數(shù)用于錯(cuò)誤單詞的定位。(3)刪除空格類字符,包括回車、制表符空格。
(4)按拼寫單詞,并用(內(nèi)碼,屬性)二元式表示。(屬性值——token的機(jī)內(nèi)表示)(5)如果發(fā)現(xiàn)錯(cuò)誤則報(bào)告出錯(cuò)
(6)根據(jù)需要是否填寫標(biāo)識(shí)符表供以后各階段使用。單詞的基本分類: ? ? ? ? ? ? 關(guān)鍵字:由程序語言定義的具有固定意義的標(biāo)識(shí)符。也稱為保留字例如 標(biāo)識(shí)符:用以表示各種名字,如變量名、數(shù)組名、函數(shù)名; 常數(shù): 任何數(shù)值常數(shù)。如 125, 1,0.5,3.1416; 運(yùn)算符:+、-、*、/;
關(guān)系運(yùn)算符: <、<=、=、>、>=、<>; 分界符: ;、,、(、)、[、]; if、for、while、printf ;
單詞種別碼為1。
三、實(shí)驗(yàn)要求
1、編程時(shí)注意編程風(fēng)格:空行的使用、注釋的使用、縮進(jìn)的使用等。
2、將標(biāo)識(shí)符填寫的相應(yīng)符號(hào)表須提供給編譯程序的以后各階段使用。
3、根據(jù)測試數(shù)據(jù)進(jìn)行測試。測試實(shí)例應(yīng)包括以下三個(gè)部分: ? 全部合法的輸入。? 各種組合的非法輸入。? 由記號(hào)組成的句子。
4、詞法分析程序設(shè)計(jì)要求輸出形式: 例:輸入VC++語言的實(shí)例程序:
If i=0 then
n++;a﹤= 3b %);輸出形式為:
單詞
二元序列
類 型
(單詞種別,單詞屬性)
for
(1,for)
關(guān)鍵字
i
(6,i)
標(biāo)識(shí)符
=
(4,=)
關(guān)系運(yùn)算符0
(5,0)
常數(shù)
then
(1,then)
關(guān)鍵字 n
(6,n)
標(biāo)識(shí)符
++
Error
Error
;
(2,;)
分界符
位置(行,列)
(1,1)
(1,2)
(1,3)
(1,4)
(1,5)
(1,6)
(1,7)
(1,8)
a
(6,a)
標(biāo)識(shí)符
(2,1)﹤=
(4,<=)
關(guān)系運(yùn)算符
(2,2)3b
Error
Error
(2,4)%
Error
Error
(2,4))
(2,))
分界符
(2,5);
(2,;)
分界符
(2,6)
實(shí)驗(yàn)報(bào)告正文:
? 功能描述:該程序具有詞法分析功能,即面對(duì)一段程序源代碼,通過該程序,能檢查出源代碼是否由詞法錯(cuò)誤。
?
三、詞法分析實(shí)驗(yàn)設(shè)計(jì)思想及算法: ? 首先構(gòu)造六個(gè)表,key[]={“auto”,“break”,“case”,“catch”,“char”,“class”,“const”,“continue”,“default”,“delete”,“do”,“double”,“else”,“enum”,“float”,“for”,“if”,“int”,“l(fā)ong”,“new”,“private”,“protected”,“public”,“register”,“return”,“short”,“static”,“struct”,“switch”,“this”,“void”,“while”,“then”};關(guān)鍵字表, 單詞種別碼1;
Delimiter[]={“;”,“(”,“)”,“[”,“]”,“,”,“.”,“{”,“}”};分界符表 單詞種別碼2 Operator[]={“+”,“-”,“*”,“/”};算術(shù)運(yùn)算符表
單詞種別碼3 R_operators[]={“<”,“<=”,“==”,“>”,“>=”},關(guān)鍵字表 , 單詞種別碼1;string Number[100];常數(shù)表
單詞種別碼5;string Identifier[100];標(biāo)示符表
單詞種別碼6;構(gòu)造關(guān)鍵字判斷函數(shù)Iskey(),字母判斷函數(shù)Isletter(),數(shù)字判斷函數(shù)Isnumber();構(gòu)造標(biāo)示符判別函數(shù)InsertId(),若輸入的標(biāo)示符在標(biāo)示符數(shù)組Identifier []中,返回其下標(biāo),若不在,將該標(biāo)示符插到數(shù)組末尾。構(gòu)造標(biāo)示符判別函數(shù)InsertNumber(),若輸入的數(shù)字在數(shù)字?jǐn)?shù)組Number[]中,返回其下標(biāo),若不在,將該數(shù)字插到數(shù)組末尾。具體分析函數(shù)analyse()具體實(shí)現(xiàn)輸入源代碼的識(shí)別。anaiyse()構(gòu)造思路,程序設(shè)計(jì)圖:
綜合以上分析,畫出整個(gè)程序的運(yùn)行分析程序圖,如下: 開始 輸入源文件路徑否 路徑是否有效是打開源文件初始化文件指針識(shí)別指針內(nèi)容文件結(jié)束?否是空格,空白或換行嗎是跳過該字符是結(jié)束否是字母嗎是將字符加入字符數(shù)組Word[]否是數(shù)字嗎否是界符嗎否將字符加入字符數(shù)組Word[]是將字符加入字符數(shù)組Word[]是指向下一字符識(shí)別指針內(nèi)容是輸出word為界符輸出Word內(nèi)容為不可識(shí)別將字符加入字符數(shù)組Word[]將字符加入字符數(shù)組Word[]指向下一字符指向下一字符是字母惑數(shù)字嗎回退否將word與關(guān)鍵字表key進(jìn)行匹配輸出word為普通標(biāo)示符是數(shù)字嗎否輸出word為常數(shù)指向下一字符否匹配?是輸出word為關(guān)鍵字整個(gè)程序的運(yùn)行分析程序圖
? 軟件的測試方法和測試結(jié)果:
首先,將要分析的源代碼寫入一個(gè)文本,存于磁盤中,然后運(yùn)行程序,輸入源代碼文件存放的路徑,若輸入路徑正確,程序?qū)⒆詣?dòng)分析源代碼,若輸入路徑不正確,程序?qū)@示,路徑錯(cuò)誤,請(qǐng)重新輸入的提示。下面為具體的運(yùn)行實(shí)例: 源代碼為:If i=0 then
n++;a﹤= 3b %)
輸出滿足要求。
? 實(shí)驗(yàn)總結(jié)(設(shè)計(jì)的特點(diǎn)、不足、收獲與體會(huì)):
通過此次實(shí)驗(yàn),讓我了解到如何設(shè)計(jì)、編制并調(diào)試詞法分析程序,熟悉了構(gòu)造詞法分析程序的手工方式的相關(guān)原理,加深了對(duì)編譯原理詞法分析的理解,本次使用C++語言直接編寫此法分析程序,也讓我重新熟悉了C++語言的相關(guān)內(nèi)容,加深了對(duì)C++語言的用途的理解。本程序的數(shù)據(jù)輸入采取直接從文件中讀取,而不是由鍵盤輸入,因此在測試過程中,輸入得到大大簡化,但是本程序的關(guān)鍵字表只初始化了一部分關(guān)鍵字,還可繼續(xù)擴(kuò)充(只需擴(kuò)大數(shù)組,向其中補(bǔ)充要添加的關(guān)鍵字),而且程序的測試數(shù)據(jù)存在不足,程序可能存在未發(fā)現(xiàn)的漏洞,以上兩點(diǎn)有待改善。附錄該程序的源代碼: #include 一.實(shí)驗(yàn)序號(hào):《編譯原理》第一次實(shí)驗(yàn) 二.實(shí)驗(yàn)題目:詞法分析 三.實(shí)驗(yàn)日期:2010.10 四.實(shí)驗(yàn)環(huán)境(操作系統(tǒng),開發(fā)語言) 操作系統(tǒng):Windows 開發(fā)語言:C 五.實(shí)驗(yàn)內(nèi)容(實(shí)驗(yàn)要求) a)將標(biāo)識(shí)符的詞法改為“以大寫字母或小寫字母開頭,后面可以跟大寫字 母或小寫字母或數(shù)字或下劃線”。 b)將<條件>中的表示相等關(guān)系的單詞“=”改為“= =” c)將原來的無小數(shù)的數(shù)改為可以有小數(shù)的數(shù) 六.實(shí)驗(yàn)步驟 a)打開VC++,找到getsym()項(xiàng)目。 int getsym() {...} 在getsym()函數(shù)中設(shè)置斷點(diǎn),F(xiàn)10逐過程調(diào)試 根據(jù)要求a修改為 if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z') 往后面看,修改while(ch>='a' && ch<='z' || ch>='0' && ch<='9' ||ch>='A' && ch<='Z' || ch=='_'); b)在檢測賦值符號(hào)的程序段中找到等號(hào) = 的判斷代碼,改變成為 = = 即 可。 c)在獲取整數(shù)數(shù)字的值的程序段后面添加判斷小數(shù)點(diǎn)的代碼。如果是,即 讀取符號(hào)并且按照小數(shù)的要求將其縮小并且累加到總的符號(hào)串中。 七.實(shí)驗(yàn)體會(huì)(包括收獲、心得體會(huì)、存在的問題及解決問題的方法、建議等) 通過實(shí)驗(yàn),如果要修改代碼的話,首先應(yīng)該讀懂源碼,在修改之前了解到程序段的功能是什么,然后再在相應(yīng)的行進(jìn)行修改添加,再進(jìn)行合理的調(diào)試。如果問題太過困難可以查詢資料或與同學(xué)進(jìn)行討論 八.實(shí)驗(yàn)結(jié)果(關(guān)鍵源程序) a) int getsym() { int i,j,k; while(ch==' '||ch==10||ch==9)/*忽略空格,換行和TAB*/{ getchdo; } if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')/*名字或保留字以a..zA。Z開頭*/{ k=0; do{ if(k { a[k]=ch; k++; } getchdo; }while(ch>='a' && ch<='z' || ch>='0' && ch<='9' || ch>='A' && ch<='Z' || ch=='_');// if(ch>='0'&&ch<='9')/*檢測是否為數(shù)字,以0..9開頭*/{ k=0; num=0; sym=number; do{ //435 num=10*num+ch-'0'; k++; getchdo; }while(ch>='0' && ch<='9'); if(ch=='.') { k++; sym=period; getchdo; sym=number; int div=10; do{num=num+(ch-'0')/div; k++; getchdo; div=div*10; }while(ch>='0'&&ch<='9'); } b) else { if(ch=='>')//檢測大于或大于等于符號(hào){ getchdo; if(ch=='=')//add by M { sym=geq;//構(gòu)成>= getchdo; } else { sym=gtr;//否則就是一個(gè)單獨(dú)的>號(hào)} } else { if(ch=='=') { getchdo; if(ch=='=') { sym=deq; getchdo; } else { sym=eql; } } c)else {if(ch == ’.’) {k=10; getchdo; do{num=num+(ch-'0')/k; k=k*10; getchdo; }while(ch>='0'&&ch<='9');} } 詞法分析是編譯器工作的第一階段,它的工作就是從輸入(源代碼)中取得token,以作為parser(語法分析)的輸入,一般在詞法分析階段都會(huì)把一些無用的空白字符(white space,即空格、tab和換行)以及注釋剔除,以降低下一步分析的復(fù)雜度,詞法分析器一般會(huì)提供一個(gè)gettoken()這樣的方法,parser可以在做語法分析時(shí)調(diào)用詞法分析器的這個(gè)方法來得到下一個(gè)token,所以詞法分析器并不是一次性遍歷所有源代碼,而是采取這種on-demand的方式:只在parser需要時(shí)才工作,并且每次只取一個(gè)token。 token和lexeme 首先,token不等于lexeme。token和lexeme的關(guān)系就類似于面向?qū)ο笳Z言中“類”和“實(shí)例”(或“對(duì)象”)之間的關(guān)系,這個(gè)用中文不知該如何解釋才好,比如語言中的變量a和b,它們都屬于同一種token:identifier,而a的lexeme是”a”,b則是”b”,而每個(gè)關(guān)鍵字都是一種token。token可以附帶有一個(gè)值屬性,例如變量a,當(dāng)調(diào)用詞法分析器的gettoken()時(shí),會(huì)返回一個(gè)identifier類型的token,這個(gè)token帶有一個(gè)屬性“a”,屬性可以是多樣的,例如表示數(shù)字的token可以帶有一個(gè)表示數(shù)字值的屬性,它是整型的。 如下代碼: int age = 23; int count = 50; 可以依次提取出8個(gè)token:int(值為”int”),id(值為”age”),assign(值為”=”),number(值為整型數(shù)值23),int(值為”int”),id(值為”count”),assign(值為”=”),number(值為50) 正則表達(dá)式 正則表達(dá)式可以用來描述字符串模式,例如我們可以用digit+來表示number的token,其中digit表示單個(gè)數(shù)字(這里說正則表達(dá)式并不完全和實(shí)現(xiàn)的正則引擎所識(shí)別的正則表達(dá)式等價(jià),這里只是為了描述問題而已)。 然而像c語言的的多行注釋,用正則表達(dá)式來描述就比較麻煩,此時(shí)更傾向于直接用有窮自動(dòng)機(jī)(finite automaton)來描述,因?yàn)橛盟鼇砻枋龇浅V庇^且很容易。 有窮自動(dòng)機(jī)(finite automata) 有窮自動(dòng)機(jī)也稱為有限狀態(tài)機(jī),狀態(tài)在輸入字符的作用下發(fā)生遷移,因此,它可以用來識(shí)別token,也因此,我們只要畫得出fa,之后再用代碼實(shí)現(xiàn)這個(gè)fa,那詞法分析器也就差不多弄好了。 有窮自動(dòng)機(jī)分確定性(dfa)和非確定性(nfa)兩種,如果對(duì)于同一個(gè)輸入,只會(huì)有一個(gè)確定的狀態(tài)遷移路線,也就是只有一個(gè)確定的“下一狀態(tài)”,那就是dfa,否則就是nfa。 因?yàn)閐fa對(duì)于同一個(gè)輸入只有一個(gè)確定的下一狀態(tài),所以詞法分析器當(dāng)然優(yōu)先采用它,那nfa拿來干嘛用呢?nfa用來做描述用時(shí)更方便,我們可以非常迅速地畫出一個(gè)識(shí)別token的nfa圖,但要想直接畫出個(gè)dfa那要?jiǎng)硬簧倌X筋。 根據(jù)正則表達(dá)式構(gòu)建nfa 如上所述,nfa更容易畫出,那我們就先研究nfa,在定義token時(shí),我們可以用正則表達(dá)式來描述它,因?yàn)檎齽t表達(dá)式干這行很合適,例如一個(gè)digit+就可以描述數(shù)字,多方便。因此,我們需要根據(jù)正則表達(dá)式畫出與之等價(jià)的nfa。而這個(gè)算法非常簡單,就是tompson’s construction,這個(gè)書上寫得很清楚了。 將nfa轉(zhuǎn)化成dfa(nfa的確定化) 對(duì)于計(jì)算機(jī)來說,面對(duì)同一個(gè)輸入,如果有多個(gè)下一狀態(tài),那計(jì)算機(jī)就不清楚要轉(zhuǎn)到哪個(gè)狀態(tài),所以我們期望能從正則表達(dá)式得到dfa,而不是nfa,因?yàn)檫@樣將來編程實(shí)現(xiàn)時(shí)比較自然(同一輸入有確定的一個(gè)下一狀態(tài)),而幸運(yùn)的是,每個(gè)nfa都可以轉(zhuǎn)化成dfa。為什么nfa可以轉(zhuǎn)化成dfa?因?yàn)閒a(finite automata)中的狀態(tài)都是我們自己畫的,只要fa能正確的識(shí)別token,那就ok了,也就是,如果nfa和dfa都可以達(dá)到一樣的效果:識(shí)別token,那其它的我們就不管了。 實(shí)現(xiàn)詞法分析器 對(duì)于一個(gè)token,比如用來表示數(shù)字的token:num,我們可以用正則表達(dá)式描述它,然后畫出nfa,再將nfa轉(zhuǎn)化成dfa,再最小化dfa的狀態(tài),但是我們的詞法分析器是不是分析一個(gè)token,所以我們要把所有類型的token的dfa合并成一個(gè)dfa,這樣,這個(gè)dfa也就可以識(shí)別語言的所有token了,如果在某一連串的輸入下,dfa達(dá)不到終結(jié)狀態(tài),那就說明源代碼有錯(cuò)誤了。 我用c#實(shí)現(xiàn)了一個(gè)用于《compiler construction: principles and practice》中tiny語言的詞法分析器,tiny語言有關(guān)鍵字:if, then, else, end, repeat, until, read, write,有操作符+,-,*,/,=,<,(,),;,:=(全角逗號(hào)不算,是文章的分隔符)這10個(gè),然后其余的token有number(一或多個(gè)數(shù)字)和identifier(一或多個(gè)字母),其dfa如下圖: 上面這張圖和《編譯原理及實(shí)踐》中的一樣,其中的帶中括號(hào)的輸入說明這個(gè)輸入是lookahead的,在匹配成功后是要重新放回輸入流中的,比如識(shí)別num時(shí),如果發(fā)現(xiàn)個(gè)非digit的,那就說明識(shí)別到了一個(gè)number,但是最后識(shí)別的那個(gè)非digit字符是要放回輸入流的,因?yàn)樗糁乱淮巫R(shí)別。 其中從start到done的那個(gè)other,指所有非white space,非{,非letter,非digit,也非:的字符,它有可能是合法的+, *, /這些,也可能是不合法的其它輸入,如#號(hào)。因此,done這個(gè)狀態(tài)只是說本次gettoken已經(jīng)結(jié)束,狀態(tài)機(jī)是有可能因?yàn)椴缓戏ǖ妮斎攵M(jìn)入done狀態(tài)的。究竟從start到done是因?yàn)楹戏ǖ模?號(hào)導(dǎo)致的,還是由不合法的如#號(hào)導(dǎo)致的,將在代碼中實(shí)現(xiàn)判斷,但可以肯定的是,不管是+號(hào)還是#號(hào)作用于start狀態(tài),都會(huì)進(jìn)入done狀態(tài)。 1.Struts2架構(gòu)圖和請(qǐng)求處理流程 請(qǐng)求首先通過Filter chain,F(xiàn)ilter主要包括ActionContextCleanUp,它主要清理當(dāng)前線程的ActionContext和Dispatcher;FilterDispatcher主要通過AcionMapper來決定需要調(diào)用哪個(gè)Action。 ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里創(chuàng)建ActionProxy,ActionProxy創(chuàng)建ActionInvocation,然后ActionInvocation調(diào)用Interceptors,執(zhí)行Action本身,創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實(shí)現(xiàn)PreResultListener。 2.Struts2部分類介紹 這部分從Struts2參考文檔中翻譯就可以了。 ActionMapper ActionMapper其實(shí)是HttpServletRequest和Action調(diào)用請(qǐng)求的一個(gè)映射,它屏蔽了Action對(duì)于Request等java Servlet類的依賴。Struts2中它的默認(rèn)實(shí)現(xiàn)類是DefaultActionMapper,ActionMapper很大的用處可以根據(jù)自己的需要來設(shè)計(jì)url格式,它自己也有Restful的實(shí)現(xiàn),具體可以參考文檔的docs¥actionmapper.html。 ActionProxy&ActionInvocation Action的一個(gè)代理,由ActionProxyFactory創(chuàng)建,它本身不包括Action實(shí)例,默認(rèn)實(shí)現(xiàn)DefaultActionProxy是由ActionInvocation持有Action實(shí)例。ActionProxy作用是如何取得Action,無論是本地還是遠(yuǎn)程。而ActionInvocation的作用是如何執(zhí)行Action,攔截器的功能就是在ActionInvocation中實(shí)現(xiàn)的。 ConfigurationProvider&Configuration ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其實(shí)現(xiàn)類XmlConfigurationProvider及其子類StrutsXmlConfigurationProvider來解析。 3.Struts2請(qǐng)求流程 1、客戶端發(fā)送請(qǐng)求 2、請(qǐng)求先通過ActionContextCleanUp-->FilterDispatcher 3、FilterDispatcher通過ActionMapper來決定這個(gè)Request需要調(diào)用哪個(gè)Action 4、如果ActionMapper決定調(diào)用某個(gè)Action,F(xiàn)ilterDispatcher把請(qǐng)求的處理交給ActionProxy,這兒已經(jīng)轉(zhuǎn)到它的Delegate--Dispatcher來執(zhí)行 5、ActionProxy根據(jù)ActionMapping和ConfigurationManager找到需要調(diào)用的Action類 6、ActionProxy創(chuàng)建一個(gè)ActionInvocation的實(shí)例 7、ActionInvocation調(diào)用真正的Action,當(dāng)然這涉及到相關(guān)攔截器的調(diào)用 8、Action執(zhí)行完畢,ActionInvocation創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實(shí)現(xiàn)PreResultListener。添加PreResultListener可以在Interceptor中實(shí)現(xiàn)。 首先強(qiáng)調(diào)一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個(gè)線程都有自己獨(dú)立的實(shí)例變量,互不相干.接下來就從Dispatcher開始看起,先看其構(gòu)造函數(shù): //創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數(shù) this.initParams = initParams; } //創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數(shù) this.initParams = initParams; } 我們?cè)倏丛贔ilterDispatcher創(chuàng)建Dispatcher的: protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以從FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以從FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } 分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() 創(chuàng)建Dispatcher之后,來看init()方法 init()方法是用來Load用戶配置文件,資源文件以及默認(rèn)的配置文件.主要分七步走,看下面注釋 public void init(){ if(configurationManager == null){ //設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進(jìn)來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構(gòu)建一個(gè)用于依賴注射的Container對(duì)象 //在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法 //其中的重點(diǎn)就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } } public void init(){ if(configurationManager == null){ //設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進(jìn)來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構(gòu)建一個(gè)用于依賴注射的Container對(duì)象 //在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法 //其中的重點(diǎn)就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } } 分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ throw } loadSettings(props, defaultSettings); } private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); throw } new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); loadSettings(props, defaultSettings); } //PropertiesSettings構(gòu)造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings // Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props //這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); props.setProperty(name, settings.getLocationImpl(name)); } } //PropertiesSettings構(gòu)造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); settings.getImpl(name),// Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props //這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); props.setProperty(name, settings.getLocationImpl(name)); } } 再來看第二步:init_TraditionalXmlConfigurations() private void init_TraditionalXmlConfigurations(){ settings.getImpl(name), //首先讀取web.xml中的config初始參數(shù)值 //如果 沒 有 配 置 就 使 用 默 認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了 //如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider負(fù)責(zé)解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider來解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } private void init_TraditionalXmlConfigurations(){ //首先讀取web.xml中的config初始參數(shù)值 //如果 沒 有 配 置 就 使 用 默 認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了 //如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider負(fù)責(zé)解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider來解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } 對(duì)于其它配置文件只用接口。 類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的 public static Document parse(InputSource inputSource, Map addAction()方法負(fù)責(zé)讀取 loadInterceptorStack()方法負(fù)責(zé)將 StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實(shí)現(xiàn)ConfigurationProviderloadInterceptorStacks()方法負(fù)責(zé)將 而上面的方法最終會(huì)被addPackage()方法調(diào)用,addPackage又會(huì)被Provider的loadPackages()調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對(duì)象中。 protected PackageConfig addPackage(Element packageElement) throws ConfigurationException { PackageConfig.Builder newPackage = buildPackageContext(packageElement); if(newPackage.isNeedsRefresh()){ return newPackage.build(); } // add result types(and default result)to this package addResultTypes(newPackage, packageElement); // load the interceptors and interceptor stacks for this package loadInterceptors(newPackage, packageElement); // load the default interceptor reference for this package loadDefaultInterceptorRef(newPackage, packageElement); // load the default class ref for this package loadDefaultClassRef(newPackage, packageElement); // load the global result list for this package loadGlobalResults(newPackage, packageElement); // load the global exception handler list for this package loadGobalExceptionMappings(newPackage, packageElement); // get actions NodeList actionList = packageElement.getElementsByTagName(“action”); for(int i = 0;i < actionList.getLength();i++){ Element actionElement =(Element)actionList.item(i); addAction(actionElement, newPackage); } // load the default action reference for this package loadDefaultActionRef(newPackage, packageElement); PackageConfig cfg = newPackage.build(); configuration.addPackageConfig(cfg.getName(), cfg); return cfg; } loadConfigurationFiles解析讀取xml中的內(nèi)容 private List loadConfigurationFiles(String fileName,Element includeElement){ ...//通過DomHelper調(diào)用SAX進(jìn)行解析xml doc = DomHelper.parse(in, dtdMappings); ...Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for(int i = 0;i < childSize;i++){ Node childNode = children.item(i); if(childNode instanceof Element){ Element child =(Element)childNode; final String nodeName = child.getNodeName(); if(“include”.equals(nodeName)){ String includeFileName = child.getAttribute(“file”); //解析每個(gè)action配置是,對(duì)于include文件可以使用通配符*來進(jìn)行配置 //如Struts.xml中可配置成 if(includeFileName.indexOf('*')!=-1){ ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Vector for(String match : wildcardMatches){ //遞歸Load子file中的 docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); ...return docs; } 首先強(qiáng)調(diào)一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個(gè)線程都有自己獨(dú)立的實(shí)例變量,互不相干.接下來就從Dispatcher開始看起,先看其構(gòu)造函數(shù): //創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數(shù) this.initParams = initParams; } //創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數(shù) this.initParams = initParams; } 我們?cè)倏丛贔ilterDispatcher創(chuàng)建Dispatcher的: protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以從FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以從FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } 分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() 創(chuàng)建Dispatcher之后,來看init()方法 init()方法是用來Load用戶配置文件,資源文件以及默認(rèn)的配置文件.主要分七步走,看下面注釋 public void init(){ if(configurationManager == null){ //設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等 configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進(jìn)來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構(gòu)建一個(gè)用于依賴注射的Container對(duì)象 //在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法 //其中的重點(diǎn)就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } new } public void init(){ if(configurationManager == null){ //設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等 configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進(jìn)來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構(gòu)建一個(gè)用于依賴注射的Container對(duì)象 //在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法 //其中的重點(diǎn)就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } new } 分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ throw } loadSettings(props, defaultSettings); } private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ throw } loadSettings(props, defaultSettings); } //PropertiesSettings構(gòu)造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings // Load settings InputStream in = null; try { = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props //這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); props.setProperty(name, settings.getLocationImpl(name)); } } //PropertiesSettings構(gòu)造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); settings.getImpl(name),if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings // Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props //這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); props.setProperty(name, settings.getLocationImpl(name)); } } 再來看第二步:init_TraditionalXmlConfigurations() private void init_TraditionalXmlConfigurations(){ //首先讀取web.xml中的config初始參數(shù)值 //如果 沒 有 配 置 就 使 settings.getImpl(name),用默認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了 //如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider負(fù)責(zé)解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider來解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } private void init_TraditionalXmlConfigurations(){ //首先讀取web.xml中的config初始參數(shù)值 //如果 沒 有 配 置 就 使 用 默 認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了 //如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider負(fù)責(zé)解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider來解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } 對(duì)于其它配置文件只用接口。 類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的 public static Document parse(InputSource inputSource, Map addAction()方法負(fù)責(zé)讀取 loadInterceptorStack()方法負(fù)責(zé)將 loadInterceptorStacks()方法負(fù)責(zé)將 而上面的方法最終會(huì)被addPackage()方法調(diào)用,addPackage又會(huì)被Provider的loadPackages()調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對(duì)象中。 protected PackageConfig addPackage(Element packageElement) throws ConfigurationException { PackageConfig.Builder newPackage = buildPackageContext(packageElement); if(newPackage.isNeedsRefresh()){ return newPackage.build(); } // add result types(and default result)to this package addResultTypes(newPackage, packageElement); // load the interceptors and interceptor stacks for this package loadInterceptors(newPackage, packageElement); // load the default interceptor reference for this package loadDefaultInterceptorRef(newPackage, packageElement); // load the default class ref for this package loadDefaultClassRef(newPackage, packageElement); // load the global result list for this package loadGlobalResults(newPackage, packageElement); // load the global exception handler list for this package loadGobalExceptionMappings(newPackage, packageElement); // get actions NodeList actionList = packageElement.getElementsByTagName(“action”); for(int i = 0;i < actionList.getLength();i++){ Element actionElement =(Element)actionList.item(i); addAction(actionElement, newPackage); } // load the default action reference for this package loadDefaultActionRef(newPackage, packageElement); PackageConfig cfg = newPackage.build(); configuration.addPackageConfig(cfg.getName(), cfg); return cfg; } loadConfigurationFiles解析讀取xml中的內(nèi)容 private List loadConfigurationFiles(String fileName, includeElement){ ...//通過DomHelper調(diào)用SAX進(jìn)行解析xml doc = DomHelper.parse(in, dtdMappings); ...Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for(int i = 0;i < childSize;i++){ Node childNode = children.item(i); if(childNode instanceof Element){ Element child =(Element)childNode; final String nodeName = child.getNodeName(); if(“include”.equals(nodeName)){ String includeFileName = child.getAttribute(“file”); //解析每個(gè)action配置是,對(duì)于include文件可以使用通配符*來進(jìn)行配置 //如Struts.xml中可配置成 if(includeFileName.indexOf('*')!=-1){ ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Element Vector for(String match : wildcardMatches){ //遞歸Load子file中的 docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); ...return docs; } 接下來第三步:init_LegacyStrutsProperties()調(diào)用的是調(diào)用的是LegacyPropertiesConfigurationProvider 通過比較前 面 DefaultPropertiesProvider 與 調(diào) 用的是LegacyPropertiesConfigurationProvider.發(fā)現(xiàn)DefaultPropertiesProvider繼承自后者,但重寫了register()方法,主要是生成PropertiesSetting的不同,前者是根據(jù)org/apache/struts2/default.properties 后者是根據(jù)struts.properties 我們展開register()中的Settings.getInstance(),最后是調(diào)用getDefaultInstance() private static Settings getDefaultInstance(){ if(defaultImpl == null){ // Create bootstrap implementation //不帶參數(shù)的DefaultSettings(),區(qū)別與DefaultPropertiesProvider中直接帶default.properties參數(shù) //不帶參數(shù)就是默認(rèn)為struts.propertes,并且加載struts.custom.properties所定義的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION為:struts.configuration //在struts.proterties中查找struts.configuration的值,這個(gè)值必須是org.apache.struts2.config.Configuration接口的實(shí)現(xiàn)類 //所以我有個(gè)困惑就是在下面的轉(zhuǎn)換當(dāng)中怎么將Configuration轉(zhuǎn)換成Setting類型的...//這一點(diǎn)先放下了,有時(shí)間再研究 String className = get(StrutsConstants.STRUTS_CONFIGURATION); if(!className.equals(defaultImpl.getClass().getName())){ try { // singleton instances shouldn't be built accessing request or session-specific context data defaultImpl oader().loadClass(className), null); } catch(Exception e){ LOG.error(“Settings: } } } catch(IllegalArgumentException ex){ // ignore } private static Settings getDefaultInstance(){ if(defaultImpl == null){ // Create bootstrap implementation //不帶參數(shù)的DefaultSettings(),區(qū)別與DefaultPropertiesProvider中直接帶default.properties參數(shù) //不帶參數(shù)就是默認(rèn)為struts.propertes,并且加載struts.custom.properties所定義的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION為:struts.configuration //在struts.proterties中查找struts.configuration的值,這個(gè)值必須是 Could not instantiate the struts.configuration object, substituting the default implementation.”, e); = (Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的實(shí)現(xiàn)類 //所以我有個(gè)困惑就是在下面的轉(zhuǎn)換當(dāng)中怎么將Configuration轉(zhuǎn)換成Setting類型的...//這一點(diǎn)先放下了,有時(shí)間再研究 String className = get(StrutsConstants.STRUTS_CONFIGURATION); if(!className.equals(defaultImpl.getClass().getName())){ try { // singleton instances shouldn't be built accessing request or session-specific context data defaultImpl oader().loadClass(className), null); } catch(Exception e){ LOG.error(“Settings: } } } catch(IllegalArgumentException ex){ // ignore } 在2.1.6中去掉了第四步:init_ZeroConfiguration();第五步是自定義的configProviders private void init_CustomConfigurationProviders(){ //從這里可以看到可以將自定義的Provider定義在web.xml中FilterDispatcher的param中:configProviders String configProvs = initParams.get(”configProviders“); if(configProvs!= null){ String[] classes = configProvs.split(”¥¥s*[,]¥¥s*“); for(String cname : classes){ try { Class cls = ClassLoaderUtils.loadClass(cname, this.getClass()); ConfigurationProvider(ConfigurationProvider)cls.newInstance(); configurationManager.addConfigurationProvider(prov); prov = Could not instantiate the struts.configuration object, substituting the default implementation.”, e); = (Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassL } ...} } } private void init_CustomConfigurationProviders(){ //從這里可以看到可以將自定義的Provider定義在web.xml中FilterDispatcher的param中:configProviders String configProvs = initParams.get(“configProviders”); if(configProvs!= null){ String[] classes = configProvs.split(“¥¥s*[,]¥¥s*”); for(String cname : classes){ try { Class cls = ClassLoaderUtils.loadClass(cname, this.getClass()); ConfigurationProvider(ConfigurationProvider)cls.newInstance(); configurationManager.addConfigurationProvider(prov); } ...} } } 第六步:init_FilterInitParameters //從這里可以看出struts.properties中的屬性不僅可以在struts.xml中以constant形式定義,而且可以在FilterDispatcher的param中定義 private void init_FilterInitParameters(){ configurationManager.addConfigurationProvider(new ConfigurationProvider(){ public void destroy(){} public void init(Configuration configuration) throws ConfigurationException {} public void loadPackages()throws ConfigurationException {} public boolean needsReload(){ return false;} prov = public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException { props.putAll(initParams);//在這里實(shí)現(xiàn)滴~ } }); } //從這里可以看出struts.properties中的屬性不僅可以在struts.xml中以constant形式定義,而且可以在FilterDispatcher的param中定義 private void init_FilterInitParameters(){ configurationManager.addConfigurationProvider(new ConfigurationProvider(){ public void destroy(){} public void init(Configuration configuration) throws ConfigurationException {} public void loadPackages()throws ConfigurationException {} public boolean needsReload(){ return false;} public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException { props.putAll(initParams);//在這里實(shí)現(xiàn)滴~ } }); } 第七步:init_AliasStandardObjects,使用BeanSelectionProvider 這是將配置文件中定義的 接下來是看怎樣調(diào)用這些ConfigurationProviders 展開init_PreloadConfiguration() private Container init_PreloadConfiguration(){ Configuration config = configurationManager.getConfiguration(); Container container = config.getContainer(); boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD)); LocalizedTextUtil.setReloadBundles(reloadi18n); return container; } //再看getConfiguration() public synchronized Configuration getConfiguration(){ if(configuration == null){ setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName)); try { //重點(diǎn)就是這個(gè)reloadContainer configuration.reloadContainer(getContainerProviders()); } catch(ConfigurationException e){ setConfiguration(null); throw new ConfigurationException(“Unable to load configuration.”, e); } } else { conditionalReload(); } return configuration; } private Container init_PreloadConfiguration(){ Configuration config = configurationManager.getConfiguration(); Container container = config.getContainer(); boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD)); LocalizedTextUtil.setReloadBundles(reloadi18n); return container; } //再看getConfiguration() public synchronized Configuration getConfiguration(){ if(configuration == null){ setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName)); try { //重點(diǎn)就是這個(gè)reloadContainer configuration.reloadContainer(getContainerProviders()); } catch(ConfigurationException e){ setConfiguration(null); throw new ConfigurationException(“Unable to load configuration.”, e); } } else { conditionalReload(); } return configuration; } 展開DefaultConfiguration中的reloadContainer public synchronized List reloadContainer(List packageContexts.clear(); loadedFileNames.clear(); List packageProviders = new ArrayList (); //Struts2(xwork2)用Container來完成依賴注入的功能 //首先初始化一個(gè)ContainerBuilder,再由builder來保存接口與實(shí)現(xiàn)類或工廠類的對(duì)應(yīng)關(guān)系 //然后通過builder.create(boolean)方法產(chǎn)生container //由container.getInstance(Class);就可以得到接口的實(shí)現(xiàn)實(shí)例了 //這一部分比較復(fù)雜,后面研究完成了,會(huì)單獨(dú)拿出來講,這里先弄清楚Xwork依賴注入的實(shí)現(xiàn)步驟就可以了 ContainerProperties props = new ContainerProperties(); ContainerBuilder builder = new ContainerBuilder(); for(final ContainerProvider containerProvider : providers) { //循環(huán)調(diào)用ConfigurationProvider的init和register方法,明白了吧,在這里統(tǒng)一循環(huán)調(diào)用 containerProvider.init(this); containerProvider.register(builder, props); } props.setConstants(builder); //注入依賴關(guān)系,在這里并不產(chǎn)生實(shí)例 builder.factory(Configuration.class, new Factory public Configuration create(Context context)throws Exception { return DefaultConfiguration.this; } }); ActionContext oldContext = ActionContext.getContext(); try { // Set the bootstrap container for the purposes of factory creation Container bootstrap = createBootstrapContainer(); setContext(bootstrap); //create已經(jīng)注入依賴關(guān)系的Container container = builder.create(false); setContext(container); objectFactory = container.getInstance(ObjectFactory.class); // Process the configuration providers first for(final ContainerProvider containerProvider : providers) { if(containerProvider instanceof PackageProvider){ container.inject(containerProvider); //調(diào)用PackageProvider的loadPackages()方法,這里主要是針對(duì)XmlConfigurationProvider和StrutsXmlConfigurationProvider ((PackageProvider)containerProvider).loadPackages(); packageProviders.add((PackageProvider)containerProvider); } } // Then process any package providers from the plugins Set packageProviderNames = container.getInstanceNames(PackageProvider.class); if(packageProviderNames!= null){ for(String name : packageProviderNames){ PackageProvider provider.init(this); provider.loadPackages(); packageProviders.add(provider); } } rebuildRuntimeConfiguration(); } finally { if(oldContext == null){ ActionContext.setContext(null); } } return packageProviders; } Dispatcher已經(jīng)在之前講過,這就好辦了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。 public void init(FilterConfig filterConfig)throws ServletException { try { this.filterConfig = filterConfig; initLogging(); //創(chuàng)建dispatcher,前面都已經(jīng)講過啰 dispatcher = createDispatcher(filterConfig); dispatcher.init(); //注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader dispatcher.getContainer().inject(this); //StaticContentLoader在BeanSelectionProvider中已經(jīng)被注入了依賴關(guān)系:DefaultStaticContentLoader //可以在struts-default.xml中的 staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig)); } finally { provider = container.getInstance(PackageProvider.class, name); ActionContext.setContext(null); } } public void init(FilterConfig filterConfig)throws ServletException { try { this.filterConfig = filterConfig; initLogging(); //創(chuàng)建dispatcher,前面都已經(jīng)講過啰 dispatcher = createDispatcher(filterConfig); dispatcher.init(); //注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader dispatcher.getContainer().inject(this); //StaticContentLoader在BeanSelectionProvider中已經(jīng)被注入了依賴關(guān)系:DefaultStaticContentLoader //可以在struts-default.xml中的 staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig)); } finally { ActionContext.setContext(null); } } //下面來看DefaultStaticContentLoader的setHostConfig public void setHostConfig(HostConfig filterConfig){ //讀取初始參數(shù) pakages,調(diào)用 parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組 String param = filterConfig.getInitParameter(“packages”); //“org.apache.struts2.static org.apache.struts2.interceptor.debugging static” String packages = getAdditionalPackages(); if(param!= null){ packages = param + “ ” + packages; } this.pathPrefixes = parse(packages); initLogging(filterConfig); } template //下面來看DefaultStaticContentLoader的setHostConfig public void setHostConfig(HostConfig filterConfig){ //讀取初始參數(shù) pakages,調(diào)用 parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組 String param = filterConfig.getInitParameter(“packages”); //“org.apache.struts2.static org.apache.struts2.interceptor.debugging static” String packages = getAdditionalPackages(); if(param!= null){ packages = param + “ ” + packages; } this.pathPrefixes = parse(packages); initLogging(filterConfig); } 現(xiàn)在回去doFilter的方法,每當(dāng)有一個(gè)Request,都會(huì)調(diào)用這些Filters的doFilter方法 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest)req; HttpServletResponse response =(HttpServletResponse)res; ServletContext servletContext = getServletContext(); String timerKey = “FilterDispatcher_doFilter: ”; try { // FIXME: this should be refactored better to not duplicate work with the action invocation //先看看ValueStackFactory所注入的實(shí)現(xiàn)類OgnlValueStackFactory //new OgnlValueStack ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); ActionContext ctx = new ActionContext(stack.getContext()); ActionContext.setContext(ctx); template UtilTimerStack.push(timerKey); //如果是multipart/form-data就用MultiPartRequestWrapper進(jìn)行包裝 //MultiPartRequestWrapper 是 StrutsRequestWrapper的子類,兩者都是HttpServletRequest實(shí)現(xiàn) //此時(shí)在MultiPartRequestWrapper中就會(huì)把Files給解析出來,用于文件上傳 //所有request都會(huì)StrutsRequestWrapper進(jìn)行包裝,StrutsRequestWrapper是可以訪問ValueStack //下面是參見Dispatcher的wrapRequest // String content_type = request.getContentType(); //if(content_type!= null&&content_type.indexOf(“multipart/form-data”)!=-1){ //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class); //request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext)); //} else { // request = new StrutsRequestWrapper(request); // } request = prepareDispatcherAndWrapRequest(request, response); ActionMapping mapping; try { //根據(jù)url取得對(duì)應(yīng)的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲(chǔ)在 ActionMapping對(duì)象中 mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); } catch(Exception ex){ log.error(“error getting ActionMapping”, ex); dispatcher.sendError(request,return; } //如果找不到對(duì)應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等 //這兒有個(gè)例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對(duì)應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再?zèng)]有則跳轉(zhuǎn)到 response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); =new 404 if(mapping == null){ // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if(“".equals(resourcePath)&& null!= request.getPathInfo()){ resourcePath = request.getPathInfo(); } if(staticResourceLoader.canHandle(resourcePath)){ // 在DefaultStaticContentLoader 中 :return serveStatic &&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“)); staticResourceLoader.findStaticResource(resourcePath, response); } else { // this is a normal request, let it pass through chain.doFilter(request, response); } // The framework did its job here return; } //正式開始Action的方法 dispatcher.serviceAction(request, response, servletContext, mapping); } finally { try { ActionContextCleanUp.cleanUp(req); } finally { UtilTimerStack.pop(timerKey); } } } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest)req; request,HttpServletResponse response =(HttpServletResponse)res; ServletContext servletContext = getServletContext(); String timerKey = ”FilterDispatcher_doFilter: “; try { // FIXME: this should be refactored better to not duplicate work with the action invocation //先看看ValueStackFactory所注入的實(shí)現(xiàn)類OgnlValueStackFactory //new OgnlValueStack ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); ActionContext ctx = new ActionContext(stack.getContext()); ActionContext.setContext(ctx); UtilTimerStack.push(timerKey); //如果是multipart/form-data就用MultiPartRequestWrapper進(jìn)行包裝 //MultiPartRequestWrapperHttpServletRequest實(shí)現(xiàn) //此時(shí)在MultiPartRequestWrapper中就會(huì)把Files給解析出來,用于文件上傳 //所有request都會(huì)StrutsRequestWrapper進(jìn)行包裝,StrutsRequestWrapper是可以訪問ValueStack //下面是參見Dispatcher的wrapRequest // String content_type = request.getContentType(); //if(content_type!= null&&content_type.indexOf(”multipart/form-data“)!=-1){ //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class); //request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext)); //} else { // request = new StrutsRequestWrapper(request); // } request = prepareDispatcherAndWrapRequest(request, response); ActionMapping mapping; try { =new 是 StrutsRequestWrapper的子類,兩者都是 //根據(jù)url取得對(duì)應(yīng)的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲(chǔ)在 ActionMapping對(duì)象中 mapping } catch(Exception ex){ log.error(”error getting ActionMapping“, ex); dispatcher.sendError(request,return; } //如果找不到對(duì)應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等 //這兒有個(gè)例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對(duì)應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再?zèng)]有則跳轉(zhuǎn)到404 if(mapping == null){ // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if(”“.equals(resourcePath)&& null!= request.getPathInfo()){ resourcePath = request.getPathInfo(); } if(staticResourceLoader.canHandle(resourcePath)){ // 在DefaultStaticContentLoader 中 :return serveStatic &&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“)); staticResourceLoader.findStaticResource(resourcePath, response); } else { // this is a normal request, let it pass through chain.doFilter(request, response); } // The framework did its job here return; } request,response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); //正式開始Action的方法 dispatcher.serviceAction(request, response, servletContext, mapping); } finally { try { ActionContextCleanUp.cleanUp(req); } finally { UtilTimerStack.pop(timerKey); } } } //下面是ActionMapper接口的實(shí)現(xiàn)類 DefaultActionMapper的getMapping()方法的源代碼: public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){ ActionMapping mapping = new ActionMapping(); String uri = getUri(request);//得到請(qǐng)求路徑的URI,如:testAtcion.action或testAction.do int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時(shí)找不到而且的bug uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri; uri = dropExtension(uri, mapping);//刪除擴(kuò)展名,默認(rèn)擴(kuò)展名為action if(uri == null){ return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉重復(fù)參數(shù) //如果Action的name沒有解析出來,直接返回 if(mapping.getName()== null){ returnnull; } //下面處理形如testAction!method格式的請(qǐng)求路徑 if(allowDynamicMethodCalls){ // handle ”name!method“ convention.String name = mapping.getName(); int exclamation = name.lastIndexOf(”!“);//!是Action名稱和方法名的分隔符 if(exclamation!=-1){ mapping.setName(name.substring(0, exclamation));//提取左邊為name mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method } } return mapping; } //下面是ActionMapper接口的實(shí)現(xiàn)類 DefaultActionMapper的getMapping()方法的源代碼: public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){ ActionMapping mapping = new ActionMapping(); String uri = getUri(request);//得到請(qǐng)求路徑的URI,如:testAtcion.action或testAction.do int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時(shí)找不到而且的bug uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri; uri = dropExtension(uri, mapping);//刪除擴(kuò)展名,默認(rèn)擴(kuò)展名為action if(uri == null){ return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉重復(fù)參數(shù) //如果Action的name沒有解析出來,直接返回 if(mapping.getName()== null){ returnnull; } //下面處理形如testAction!method格式的請(qǐng)求路徑 if(allowDynamicMethodCalls){ // handle ”name!method“ convention.String name = mapping.getName(); int exclamation = name.lastIndexOf(”!“);//!是Action名稱和方法名的分隔符 if(exclamation!=-1){ mapping.setName(name.substring(0, exclamation));//提取左邊為name mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method } } return mapping; } 從代碼中看出,getMapping()方法返回ActionMapping類型的對(duì)象,該對(duì)象包含三個(gè)參數(shù):Action的name、namespace和要調(diào)用的方法method。 如果getMapping()方法返回ActionMapping對(duì)象為null,則FilterDispatcher認(rèn)為用戶請(qǐng)求不是Action,自然另當(dāng)別論,F(xiàn)ilterDispatcher會(huì)做一件非常有意思的事:如果請(qǐng)求以/struts開頭,會(huì)自動(dòng)查找在web.xml文件中配置的 packages初始化參數(shù),就像下面這樣: org.apache.struts2.dispatcher.FilterDispatcher packages com.lizanhong.action org.apache.struts2.dispatcher.FilterDispatcher packages com.lizanhong.action FilterDispatcher會(huì)將com.lizanhong.action包下的文件當(dāng)作靜態(tài)資源處理,即直接在頁面上顯示文件內(nèi)容,不過會(huì)忽略擴(kuò)展名為class的文件。比如在com.lizanhong.action包下有一個(gè)aaa.txt的文本文件,其內(nèi)容為“中華人民共和國”,訪問 http://localhost:8081/Struts2Demo/struts/aaa.txt時(shí)會(huì)輸出txt中的內(nèi)容 FilterDispatcher.findStaticResource()方法 protectedvoid findStaticResource(String name,HttpServletRequest request, HttpServletResponse response)throws IOException { if(!name.endsWith(”.class“)){//忽略class文件 //遍歷packages參數(shù) for(String pathPrefix : pathPrefixes){ InputStream is = findInputStream(name, pathPrefix);//讀取請(qǐng)求文件流 if(is!= null){ ...// set the content-type header String contentType = getContentType(name);//讀取內(nèi)容類型 if(contentType!= null){ response.setContentType(contentType);//重新設(shè)置內(nèi)容類型 } ...try { //將讀取到的文件流以每次復(fù)制4096個(gè)字節(jié)的方式循環(huán)輸出 copy(is, response.getOutputStream()); } finally { is.close(); } return; } } } } protectedvoid findStaticResource(String name,HttpServletRequest request, HttpServletResponse response)throws IOException { if(!name.endsWith(”.class“)){//忽略class文件 //遍歷packages參數(shù) for(String pathPrefix : pathPrefixes){ InputStream is = findInputStream(name, pathPrefix);//讀取請(qǐng)求文件流 if(is!= null){ ...// set the content-type header String contentType = getContentType(name);//讀取內(nèi)容類型 if(contentType!= null){ response.setContentType(contentType);//重新設(shè)置內(nèi)容類型 } ...try { //將讀取到的文件流以每次復(fù)制4096個(gè)字節(jié)的方式循環(huán)輸出 copy(is, response.getOutputStream()); } finally { is.close(); } return; } } } } 如果用戶請(qǐng)求的資源不是以/struts開頭——可能是.jsp文件,也可能是.html文件,則通過過濾器鏈繼續(xù)往下傳送,直到到達(dá)請(qǐng)求的資源為止。 如果getMapping()方法返回有效的ActionMapping對(duì)象,則被認(rèn)為正在請(qǐng)求某個(gè)Action,將調(diào)用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關(guān)鍵所在。 下面就來看serviceAction,這又回到全局變量dispatcher中了 //Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException { //createContextMap方法主要把Application、Session、Request的key value值拷貝到Map中 Map // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action ValueStack stack = (ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); boolean nullStack = stack == null; if(nullStack){ ActionContext ctx = ActionContext.getContext(); if(ctx!= null){ stack = ctx.getValueStack(); } } if(stack!= null){ extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = ”Handling request from Dispatcher“; try { UtilTimerStack.push(timerKey); String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); //創(chuàng)建一個(gè)Action的代理對(duì)象,ActionProxyFactory是創(chuàng)建ActionProxy的工廠 //參考實(shí)現(xiàn)類:DefaultActionProxy和DefaultActionProxyFactory ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // if the ActionMapping says to go straight to a result, do it! //如果是Result,則直接轉(zhuǎn)向,關(guān)于Result,ActionProxy,ActionInvocation下一講中再分析 if(mapping.getResult()!= null){ Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { //執(zhí)行Action proxy.execute(); } // If there was a previous value stack then set it back onto the request if(!nullStack){ request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch(ConfigurationException e){ // WW-2874 Only log error if in devMode if(devMode){ LOG.error(”Could not find action or result“, e); } else { LOG.warn(”Could not find action or result“, e); } sendError(request, HttpServletResponse.SC_NOT_FOUND, e); } catch(Exception e){ sendError(request,} finally { UtilTimerStack.pop(timerKey); } } 下面開始講一下主菜ActionProxy了.在這之前最好先去了解一下動(dòng)態(tài)Proxy的基本知識(shí).ActionProxy是Action的一個(gè)代理類,也就是說Action的調(diào)用是通過ActionProxy實(shí)現(xiàn)的,其實(shí)就是調(diào)用了ActionProxy.execute()方法,而該方法又調(diào)用了ActionInvocation.invoke()方法。歸根到底,最后調(diào)用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。 最后 通 過 調(diào) 用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()這里的步驟是先由ActionProxyFactory創(chuàng)建ActionInvocation和ActionProxy.public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); return } public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext); response,context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); response,context,ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); return } 下面先看DefaultActionInvocation的init方法 public void init(ActionProxy proxy){ this.proxy = proxy; Map // Setting this so that other classes, like object factories, can use the ActionProxy and other // contextual information to operate ActionContext actionContext = ActionContext.getContext(); if(actionContext!= null){ actionContext.setActionInvocation(this); } //創(chuàng)建Action,struts2中每一個(gè)Request都會(huì)創(chuàng)建一個(gè)新的Action createAction(contextMap); if(pushAction){ stack.push(action); contextMap.put(”action“, action); } invocationContext = new ActionContext(contextMap); invocationContext.setName(proxy.getActionName()); // get a new List so we don't get problems with the iterator if someone changes the list List interceptorList = new ArrayList interceptors = interceptorList.iterator(); createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext); } protected void createAction(Map // load action String timerKey = ”actionCreate: “ + proxy.getActionName(); try { UtilTimerStack.push(timerKey); //默認(rèn)為SpringObjectFactory:struts.objectFactory=spring.這里非常巧妙,在struts.properties中可以重寫這個(gè)屬性 //在前面BeanSelectionProvider中通過配置文件為ObjectFactory設(shè)置實(shí)現(xiàn)類 //這里以Spring為例,這里會(huì)調(diào)到SpringObjectFactory的buildBean方法,可以通過ApplicationContext的getBean()方法得到Spring的Bean action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap); } catch(InstantiationException e){ throw new XWorkException(”Unable to intantiate Action!“,e, proxy.getConfig()); } catch(IllegalAccessException e){ throw new XWorkException(”Illegal access to constructor, is it public?", e, proxy.getConfig()); } catch(Exception e){ ...} finally { UtilTimerStack.pop(timerKey); } if(actionEventListener!= null){ action = actionEventListener.prepare(action, stack); } } //SpringObjectFactory public Object buildBean(String beanName, Map Object o = null; try { //SpringObjectFactory 會(huì) 通 過 web.xml 中的 一.一個(gè)簡單的“photoshop”軟件 二.設(shè)計(jì)目的: 數(shù)字圖像處理,就是用數(shù)字計(jì)算機(jī)及其他有關(guān)數(shù)字技術(shù),對(duì)圖像進(jìn)行處理,以達(dá)到預(yù)期的目的。隨著計(jì)算機(jī)的發(fā)展,圖像處理技術(shù)在許多領(lǐng)域得到了廣泛應(yīng)用,數(shù)字圖像處理已成為電子信息、通信、計(jì)算機(jī)、自動(dòng)化、信號(hào)處理等專業(yè)的重要課程。 數(shù)字圖像處理課程設(shè)計(jì)是在完成數(shù)字圖像處理的相關(guān)理論的學(xué)習(xí)后,進(jìn)行的綜合性訓(xùn)練課程,其目的主要包括: 1、使學(xué)生進(jìn)一步鞏固數(shù)字圖像處理的基本概念、理論、分析方法和實(shí)現(xiàn)方法; 2、增強(qiáng)學(xué)生應(yīng)用VC++編寫數(shù)字圖像處理的應(yīng)用程序及分析、解決實(shí)際問題的能力; 3、嘗試將所學(xué)的內(nèi)容解決實(shí)際工程問題,培養(yǎng)學(xué)生的工程實(shí)踐能力,提高工科學(xué)生的就業(yè)能力。 三.設(shè)計(jì)內(nèi)容: 1.打開圖像: 主要代碼: static char szFilter[]=“BMP文件(*.bmp)|*.bmp||”;//定義過濾文件的類型 象 CString filename; int ret=dlg.DoModal();//運(yùn)行打開文件對(duì)方框 if(ret==IDOK) { filename=dlg.GetFileName(); //獲取所選擇圖像的路徑 m_dib.LoadFromFile(filename); //加載圖像 if(!m_dib.m_bLoaded) //判斷是否加載圖像成功 { AfxMessageBox(“圖像打不開”); } return; CFileDialog dlg(TRUE,“bmp”,NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,szFilter);//定義文件對(duì)話框?qū)?/p> 效果圖: 2.水平鏡像:把圖像的第一列和最后一列調(diào)轉(zhuǎn),第二列和倒數(shù)第二列換過來,以此類推下去,直到第nw/2為止。代碼: int temp,i,j; for(j=0;j for(i=0;i { temp=m_dib.m_pdata[j*nw+i]; m_dib.m_pdata[j*nw+i]=m_dib.m_pdata[nw-i-1+j*nw]; m_dib.m_pdata[nw-i-1+j*nw]=temp; } 效果圖: 3.素描風(fēng)格:先把灰度值與右下的作對(duì)比,如果差值大于一個(gè)值則說明這是輪廓,先把非輪廓的位置像素置為黑色,最后對(duì)所有像素進(jìn)行底片化處理 代碼: int temp,i,j; for(j=0;j for(i=0;i { temp=m_dib.m_pdata[j*nw+i]-m_dib.m_pdata[(j+1)*nw+i+1]; } if(temp<10)m_dib.m_pdata[j*nw+i]=0; //黑色為0 for(j=0;j for(i=0;i 效 { int gray=m_dib.m_pdata[j*nw+i]; m_dib.m_pdata[j*nw+i]=255-gray; } 果 圖: 4圖像霧化:在圖像中引入一定的隨機(jī)值,打亂圖像中的像素值 代碼: int i,j,k,dat; //i表示列,j表行 byte *ptemp=(byte *)new byte[nw*nh]; memset(ptemp,0,nw*nh); for(j=0;j for(i=0;i { k=rand()%8;//取任意的隨機(jī)值 dat=j*nw+i+k;if(dat>=nw*nh)dat=nw*nh-1;ptemp[j*nw+i]=m_dib.m_pdata[dat]; } memcpy(m_dib.m_pdata,ptemp,nw*nh);效果圖: 5.浮雕處理:通過勾畫圖象輪廓和降低周圍像素色值,從而生成具有凹凸感的浮雕效果。其方法是生成一緩沖區(qū),計(jì)算當(dāng)前像素的左上角與右下角的像素值之差,再加上一個(gè)補(bǔ)值。將其存儲(chǔ)到緩沖區(qū)。再將緩沖區(qū)的數(shù)據(jù)逐點(diǎn)替換到圖像中并顯示出來。代碼: int w=3,i,j; //w為模板寬度 BYTE *p=new BYTE[nw*nh]; memcpy(p,m_dib.m_pdata,nw*nh); for(j=w/2;j for(i= w/2;i { p[j*nw+i]=m_dib.m_pdata[(j-1)*nw+i-1]*(1)+m_dib.m_pdata[(j+1)*nw+i+1]*(-1)+120; } memcpy(m_dib.m_pdata,p,nw*nh); delete []p;效果圖: 6.直方圖均衡化 代碼: int n[256]={0},g[256]={0};//定義頻數(shù)數(shù)組n,均衡化每個(gè)像素的灰度級(jí)的數(shù)組g double f[256],t[256];//定義頻率數(shù)組f,累加的頻率數(shù)組t int g_max=0,g_mim=255; int i,j,k,z; for(j=0;j //統(tǒng)計(jì)灰度級(jí)的頻數(shù)n for(i=0;i z=m_dib.m_pdata[j*nw+i]; n[z]++; } for(k=0;k<=255;k++) //統(tǒng)計(jì)每個(gè)灰度級(jí)出現(xiàn)的頻率 f[k]=n[k]/(nw*nh*1.0); //累計(jì)灰度級(jí)的頻率 t[0]=f[0];for(k=1;k<=255;k++) t[k]=t[k-1]+f[k]; for(j=0;j for(i=0;i g_max=w>g_max?w:g_max;//得到最大值 g_mim=w } for(k=0;k<=255;k++) //利用公式求每個(gè)像素均衡化后的灰度級(jí) g[k]=(int)((g_max-g_mim)*t[k]+g_mim+0.5); for(j=0;j //逐個(gè)替換 for(i=0;i k=m_dib.m_pdata[j*nw+i]; m_dib.m_pdata[j*nw+i]=g[k]; } for(j=0;j //計(jì)算均衡化的直方圖 { //繪制原圖像的直方圖 for(i=0;i { BYTE temp=m_dib.m_pdata[j*nw+i]; m_hist[temp]++;} m_bHist=true; CString str;int nh=m_dib.GetDIBHeight();int i;// 畫坐標(biāo)軸 // 繪制坐標(biāo)軸 pDC->MoveTo(410,nh+20);//(410,nh+20)是直方圖的左上角坐標(biāo) // 垂直軸 pDC->LineTo(410,nh+200);//(410,nh+200)是直方圖的左下角坐標(biāo) // 水平軸 pDC->LineTo(710,nh+200);//(710,nh+200)是直方圖的右下角坐標(biāo) // 寫X軸刻度值 str.Format(“0”);pDC->TextOut(410, nh+200+10, str);str.Format(“50”);pDC->TextOut(460, nh+200+10, str);str.Format(“100”);pDC->TextOut(510, nh+200+10, str);str.Format(“150”);pDC->TextOut(560, nh+200+10, str);str.Format(“200”);pDC->TextOut(610, nh+200+10, str);str.Format(“255”);pDC->TextOut(665, nh+200+10, str);// 繪制X軸刻度 for(i = 0;i < 256;i += 25){ if((i & 1)== 0){ } // 10的倍數(shù) pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2); } else { } // 10的倍數(shù) pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2);} // 繪制X軸箭頭 pDC->MoveTo(705,nh+200-5);pDC->LineTo(710,nh+200);pDC->LineTo(705,nh+200+5);// 繪制y軸箭頭 pDC->MoveTo(410,nh+20);pDC->LineTo(405,nh+20+5);pDC->MoveTo(410,nh+20);pDC->LineTo(415,nh+20+5);int max=0;for(i=0;i<256;i++)if(m_yuan[i]>max){ } max=m_yuan[i]; for(i=0;i<256;i++)pDC->MoveTo(410+i,nh+200);pDC->LineTo(410+i,nh+200-(m_yuan[i]*160/max));} if(m_bHist==true)//繪畫新的直方圖 { CString str;int nh=m_dib.GetDIBHeight();int i;// 畫坐標(biāo)軸 // 繪制坐標(biāo)軸 pDC->MoveTo(10,nh+20);//(10,nh+20)是直方圖的左上角坐標(biāo) // 垂直軸 pDC->LineTo(10,nh+200);//(10,nh+200)是直方圖的左下角坐標(biāo) // 水平軸 pDC->LineTo(310,nh+200);//(310,nh+200)是直方圖的右下角坐標(biāo) // 寫X軸刻度值 str.Format(“0”); pDC->TextOut(10, nh+200+10, str);str.Format(“50”);pDC->TextOut(60, nh+200+10, str);str.Format(“100”);pDC->TextOut(110, nh+200+10, str);str.Format(“150”);pDC->TextOut(160, nh+200+10, str);str.Format(“200”);pDC->TextOut(210, nh+200+10, str);str.Format(“255”);pDC->TextOut(265, nh+200+10, str);// 繪制X軸刻度 for(i = 0;i < 256;i += 25){ if((i & 1)== 0){ // 10的倍數(shù) } else { // 10的倍數(shù) pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2);pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2);} } // 繪制X軸箭頭 pDC->MoveTo(305,nh+200-5);pDC->LineTo(310,nh+200);pDC->LineTo(305,nh+200+5);// 繪制y軸箭頭 pDC->MoveTo(10,nh+20);pDC->LineTo(5,nh+20+5);pDC->MoveTo(10,nh+20);pDC->LineTo(15,nh+20+5);int max=0;for(i=0;i<256;i++)if(m_hist[i]>max){ max=m_hist[i]; for(i=0;i<256;i++)pDC->MoveTo(10+i,nh+200);pDC->LineTo(10+i,nh+200-(m_hist[i]*160/max)); } } 效果圖: 四.心得體會(huì): 通過這次數(shù)字圖像處理的課程設(shè)計(jì),對(duì)圖片有了更深一層的認(rèn)識(shí),理解了對(duì)圖像處理的一些原理,在這個(gè)課程設(shè)計(jì)過程中,需要自己去查閱資料,找資料,還需要理解所找到的資料,遇到問題獨(dú)立去思考,或者去請(qǐng)教同學(xué),給了我一個(gè)很好的鍛煉機(jī)會(huì),做事情一定要堅(jiān)持,最后一定會(huì)有收獲的。 五.參考文獻(xiàn): 《數(shù)字圖像處理》 ——電子工業(yè)出版社 《vc++數(shù)字圖像處理實(shí)驗(yàn)指導(dǎo)書》 曹老師、何家峰主編第二篇:《編譯原理》課程(詞法分析)實(shí)驗(yàn)報(bào)告
第三篇:詞法分析小結(jié)
第四篇:struts2代碼分析
第五篇:簡易photoshop代碼數(shù)字圖像處理實(shí)驗(yàn)報(bào)告