第一篇:Linux操作系統的權限代碼分析
現在關于內核的書很少涉及到Linux內核的安全,內核安全大概包括了密碼學實現(crypto)和訪問控制(security)兩個部分。安全系 統作為Linux內核的一個重要的子系統,已經為我們提供了很多的相關接口,這里我們就對安全訪問控制做一個簡要的分析和介紹。訪問控制的原理注定要和虛擬文件系統和進程管理有著非常緊密的聯系,因為作為用戶主體的表現形式就是進程,而作為資源客體對象的表現形式就是文件,而訪問 控制就是如何實現正確的用戶可以訪問正確的資源。Linux能夠提供給我們許多可信的方式來處理這樣的問題。
初始化工作
這個初始化工作在init/main.c中的start_kernel()中security_init()定義了,其具體的實現是在security/security.c中:
int __init security_init(void){ printk(KERN_INFO “Security Framework v” SECURITY_FRAMEWORK_VERSION “ initialized/n”);if(verify(&dummy_security_ops)){ printk(KERN_ERR “%s could not verify ” “dummy_security_ops structure./n”, __FUNCTION__);return-EIO;} security_ops = &dummy_security_ops;do_security_initcalls();return 0;} 這個函數首先用verify來驗證所指定的訪問控制策略(dummy_security_ops)是否為空,如果為空就按“保持默認”的方式進行分 配,這里的“保持沉默”就是對于任何的訪問控制采取不管不問的方式處理了。然后就是把dummy_security_ops指定給系統全局安全策略 security_ops。
訪問控制策略的相關接口
關于這些接口就是定義在了include/linux/security.h中的security_operations,包括如下一些操作:當父 進程trace子進程時進行的權限檢查,對權能的獲取、設置檢查、設置、有效性檢查,對進程做審計的檢查,當某個操作使用一般系統接口表時需要的權限檢 查,當使用內核消息環或改變登錄終端時需要的權限檢查,當改變系統時間需要的檢查,當分配一個新的虛擬內存頁需要的權限檢查,當執行二進制程序時需要的各 種權限分配和檢查,對文件系統操作時需要的各種訪問控制操作,對inode索引節點操作時需要的各種訪問控制操作,對文件操作時的各種訪問控制操作,對進 程操作的需要的各種訪問控制操作,對進程間通信信號燈的權限控制,對消息隊列的控制,對進程間通信的共享內存區域的控制,對網絡消息處理需要的各種控制,注冊與撤銷訪問控制策略,對網絡連接的控制,對套接字的各種控制,對IPSEC中xfrm用戶自定義策略的分配,密鑰管理的控制等等,幾乎囊括了系統各種 行為的控制。
權限管理
虛擬文件系統為各種類型的文件系統提供統一的操作接口,同時這樣的做法也可以簡化文件權限的管理。那么Linux時如何巧妙地實現這種想法呢?Linux 采用的是基于列的ACL自主訪問控制,即在每個文件里存儲對本文件的訪問權限信息,這里我們采用索引節點inode(定義在 include/linux/fs.h)作為切入點進行分析。在inode結構體中有i_uid和i_gid元素,還有一個i_mode元素。這個 i_mode是16位的無符號整數表示,由9位權限方式位、3位“粘滯”標志位和4位文件類型標志位,它們的具體的定義在 include/linux/stat.h中:
#define S_IFMT 00170000 /* 用于抽取i_mode域中類型部分的屏蔽位 */ #define S_IFSOCK 0140000 /* 套接字類型碼 */ #define S_IFLNK 0120000 /* 符號連接類型碼 */ #define S_IFREG 0100000 /* 普通文件類型碼 */ #define S_IFBLK 0060000 /* 塊特別文件類型碼 */ #define S_IFDIR 0040000 /* 目錄文件類型碼 */ #define S_IFCHR 0020000 /* 字符特別文件類型碼 */ #define S_IFIFO 0010000 /* 管道或FIFO類型碼 */ #define S_ISUID 0004000 /* 用戶粘滯位 */ #define S_ISGID 0002000 /* 用戶組粘滯位 */ #define S_ISVTX 0001000 /* 粘滯位 */ #define S_IRWXU 00700 /* 用戶讀寫執行 */ #define S_IRUSR 00400 /* 用戶讀 */ #define S_IWUSR 00200 /* 用戶寫 */ #define S_IXUSR 00100 /* 用戶執行 */ #define S_IRWXG 00070 /* 用戶組讀寫執行 */ #define S_IRGRP 00040 /* 用戶組讀 */ #define S_IWGRP 00020 /* 用戶組寫 */ #define S_IXGRP 00010 /* 用戶組執行 */ #define S_IRWXO 00007 /* 其他用戶讀寫執行 */ #define S_IROTH 00004 /* 其他用戶讀 */ #define S_IWOTH 00002 /* 其他用戶寫 */ #define S_IXOTH 00001 /* 其他用戶執行 */ #define S_IRWXUGO(S_IRWXU|S_IRWXG|S_IRWXO)/* 全部用戶讀寫執行 */ #define S_IALLUGO(S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)/* 全部用戶全部權限 */ #define S_IRUGO(S_IRUSR|S_IRGRP|S_IROTH)/* 全部用戶讀 */ #define S_IWUGO(S_IWUSR|S_IWGRP|S_IWOTH)/* 全部用戶寫 */ #define S_IXUGO(S_IXUSR|S_IXGRP|S_IXOTH)/* 全部用戶執行 */ 同時,每個進程的task_struct中也有對應的uid,euid,suid,fsuid,gid,egid,sgid,fsgid等元素,當用戶登錄系統就創建了一個shell進程,它從/etc/passwd中取得對應用戶的uid和gid 來唯一標志這個用戶,以后所有的進程就代代相傳。當內核在執行用戶進程訪問文件的請求時就要對比進程的uid、gid與文件的訪問模式位,由此決定該進程 是否有對文件的操作權限。uid為零的用戶為超級用戶,可以對任何資源進行管理,當然這也導致了系統安全的不完備性。
判定一個進程是否有對某個文件有某種訪問的主要工作是由fs/namei.c中的permission函數決定的,具體的實現方式如下,其中的mask參數是所要求的訪問方式位的標志位:
int permission(struct inode *inode, int mask, struct nameidata *nd){ umode_t mode = inode->i_mode;int retval, submask;if(mask & MAY_WRITE){ //假如加載的文件系統是只讀的就不允許寫,比如是磁盤設備
if(IS_RDONLY(inode)&&(S_ISREG(mode)|| S_ISDIR(mode)|| S_ISLNK(mode)))
return-EROFS;//假如加載的文件系統是不可變的就不允許寫
if(IS_IMMUTABLE(inode))return-EACCES;} //是否滿足可執行
if((mask & MAY_EXEC)&& S_ISREG(mode)&&(!(mode & S_IXUGO)||(nd && nd->mnt &&(nd->mnt->mnt_flags & MNT_NOEXEC))))return-EACCES;submask = mask & ~MAY_APPEND;//返回適應的權限位
if(inode->i_op && inode->i_op->permission)//交給了具體文件系統實現,比如說ext3文件系統
retval = inode->i_op->permission(inode, submask, nd);else //如果當前進程的fsuid與文件uid相同要比對文件屬主的權限,否則比對用戶組
retval = generic_permission(inode, submask, NULL);if(retval)return retval;//返回適應的訪問控制策略的權限位,比如說selinux return security_inode_permission(inode, mask, nd);}
第二篇:機票操作系統代碼
操作系統代碼
1,查詢航班:AVH/緊跟輸入城市段、日期(數字)、月份(英文)后回車查看。如果查詢指定航空公司月份后加“/”再加航空公司代號。
2,訂座:SD后緊跟序號計劃預定倉位跟人數后回車。(如果顯示JET代表待定航班)
3.人名:NM1后緊跟客人姓名,如果多個個客人,人名雨人名之間用數字1隔開(國際航班必須輸入英文,中國人姓在前后加/,外國人名在前)
4,聯系方式:CT后輸入聯系電話
5,預留時間:TKTL/后跟幾點/日期月份BJS…(代碼)
6,封口:@IK(封口號碼為5位數字)
7,提記錄:RT后緊跟封口號碼
8,取消訂票:XEPNR
9,價格查詢:FD:城市段(只使用于國內查詢)PAT:A 查國內稅和價格
10:查詢那些航空公司飛:SKPEK緊跟目的地
11,查詢指定日期直達航班:AV:城市段/日期月份
12,查詢經停點:IT:航班號/日期月份
13,查詢航班經停的城市起降時間和機型:FF:航班號/日期月份(沒有經停的不顯示)14,查稅(價格):QTE:/承運人(航空公司)(必須輸入完行程封口或達到上面第二步),如果出來很多倉位,在輸入XSFSQ后跟代表倉位代碼的序號。(共享的航班不能查稅)15, 查詢學生機票的稅和價格QTE:SD/航空公司
16,查詢移民機票價:QTE:EM/航空公司
17,查詢青年機票價格:QTE:ZZ/航空公司
18,OPE票的預定指令:SN:承運人---艙位---出發地與目的地
19,查詢SPA價格的指令:NFAD:城市段/CA(只能用于國航聯運協議的航空公司。國際段的查詢)
20,查匯率:XS(空格跟FSC后跟幣種代碼/人民幣(可以互換)
21,查代碼代表城市:CD:跟城市代碼
22,用姓名查找記錄:RT/旅客姓的拼音/航班號/日月年
23,SK:城市段/日期 查詢在特定周期內所有航班的信息,所顯示的航班信息時間為指定時間的前后三天一周的時間
24,查看是否出票:提記錄后,輸入PG1回車,有票號證明已經出票完畢。
25,查詢國際段航班價格指令:XSFSD(空格)行程/日期/航空公司,如果后加X,最便宜的會顯示在最前面。
26,如果沒有艙位需要候補艙位:SD后跟序號在跟艙位/LL后跟人數
CP全清屏I清上次屏PN下翻PB上翻PF最前頁PG重新顯示當前頁PL最后頁。Q值的計算方法:Q值乘以兌換率。(如果使用系統里票面價格的時候不用單獨計算Q值,因為系統里的報價已經包含全部費用,如果使用促銷價即不使用系統里顯示的價格的時候要計算Q值再加稅)
學生票:LH的Q艙位UA的V艙位 大部分情況下代表學生票
外航(例如:AC,UA,NW等)大部分是Q票面,(國際段的價格票面應該以做境外段的票務公司報出的價格為準)國航的價格看系統或大本政策。去往北美洲國航聯運的比較AC,UA等轉機的價格略高。去往歐洲的國航相對法航的要便宜,HU飛日本韓國便宜 去往東南亞國家南航便宜,北京去往韓國MU,北京到香港CZ便宜
第三篇:操作系統課程設計題目及代碼
題目一
模擬操作系統設計
設計一個模擬操作系統管理程序,實現下列管理功能: 1.內存管理功能 2.文件管理功能 3.磁盤管理功能
題目二
虛擬存儲器各頁面置換算法的實現與比較 內 容:設計一個虛擬存儲區和內存工作區,通過產生一個隨機數的方法得到一個頁面序列,假設內存給定的頁面數由鍵盤輸入,分別計算使用下述各方法時的內存命中率:
先進先出算法(FIFO)、最近最少使用算法(LRU)、最佳淘汰算法(OPT)、最少訪問頁面算法(LFU)等。
參考資料
題目二
資料
虛擬存儲器各頁面置換算法的實現與比較
1.實驗目的
存儲管理的主要功能之一是合理的分配空間。請求頁式管理是一種常用的虛擬存儲管理技術。
本實驗的目的是通過請求頁式存儲管理中頁面置換算法模擬設計,了解虛擬存儲技術的特點,掌握請求頁式存儲管理的頁面置換算法。2.實驗內容
(1)通過隨機數產生一個指令序列,共320條指令。指令的地址按下述原則生成: 1)50%的指令是順序執行的;
2)25%的指令是均勻分布在前地址部分; 3)25%的指令是均勻分布在后地址部分; 具體的實施方法是:
1)在[0,319]的指令地址之間隨機選取一起點m; 2)順序執行一條指令,即執行地址為m+1的指令;
3)在前地址[0,m+1]中隨機選取一條指令并執行,該指令的地址為m'; 4)順序執行一條指令,其地址為m'+1;
5)在后地址[m'+2,319]中隨機選取一條指令并執行; 6)重復上述步驟1)-5),直到執行320次指令。(2)將指令序列變換成為頁地址流 設:1)頁面大小為1k;
2)用戶內存容量為4頁到32頁; 3)用戶虛存容量為32k; 在用戶虛存中,按每k存放10條指令排列虛存地址,即320條指令在虛存中的存放方式為: 第0條-第9條指令為第0頁(對應虛存地址為[0,9]); 第10條-第19條指令為第1頁(對應虛存地址為[10,19]);
...第310條-第319條指令為第31頁(對應虛存地址為[310,319]);
按以上方式,用戶指令可組成為32頁。
(3)計算并輸出下列各種算法在不同內存容量下的命中率。1)先進先出的算法(FIFO); 2)最近最少使用算法(LRR);3)最佳淘汰算法(OPT):先淘汰最不常用的頁地址; 4)最少訪問頁面算法(LF.U); 5)最近最不經常使用算法(NUR)。其中3)和4)為選擇內容。命中率=1-頁面失效次數/頁地址流長度
在本實驗中,頁地址流長度為320,頁面失效次數為每次訪問相應指令時,該指令所對應的頁不在內存的次數。3.隨機數產生辦法
關于隨機數產生辦法,Linux或Unix系統提供函數srand()和rand(),分別進行初始化和產生隨機數。例如: srand();
語句可初始化一個隨機數; a[0]=10*rand()/32767*319+1;a[1]=10*rand()/32767*a[0];
..語句可用來產生a[0]與a[1]中的隨機數。
提示:
首先用Srand()和rand()函數定義和產生指令序列,然后將指令序列變換成相應的頁地址流,并針對不同的算法計算出相應的命中率。
命中率=1-頁面失效次數/頁地址流長度
1、數據結構
(1)頁面類型 typedef struct{
int pn,pfn,counter,time;}pl-type;
其中pn為頁號,pfn為頁面號,count為一個周期內訪問該頁面的次數,time為訪問時間。
(2)頁面控制結構 pfc_struct{
int pn,pfn;
struct pfc_struct *next;
};typedef struct
pfc_struct pfc_type;pfc_type
pfc[total_vp],*freepf_head,*busypf_head;pfc_type *busypf_tail;其中,pfc[total_vp]定義用戶進程虛頁控制結構,*freepf_head為空頁面頭的指針,*busypf_head為忙頁面頭的指針,*busyf_tail為忙頁面尾的指針。
2、函數定義
(1)Void initialize():初始化函數,給每個相關的頁面賦值。(2)Void FIFO():計算使用FIFO算法時的命中率。(2)Void LRU():計算使用FIFO算法時的命中率。(4)VoidOPT():計算使用OPT算法時的命中率。(5)Void LFU():計算使用LFU算法時的命中率。(6)Void
NUR():計算使用NUR算法時的命中率。
3、變量定義
(1)int a[tatal_instruction] :指令流數據組。
(2)int page[total_instruction]:每條指令所屬頁號。
(3)int offset[total_instruction]:每頁裝入不敷出0條指令后取模運算頁號偏移量。(4)int total_pf:用戶進程的內存頁面數。(5)int diseffect:頁面失效次數。
程序清單
程序: 程序: #include “stdio.h” #include “process.h” #include “stdlib.h” #define TRUE 1 #define FALSE 0 #define INVALID-1 #define null 0 #define total_instruction 320 /*指令流長*/ #define total_vp 32 /*虛頁長*/ #define clear_period 50 /*清0周期*/ typedef struct { int pn,pfn,counter,time;}pl_type;pl_type pl[total_vp];/*頁面數據結構*/ struct pfc_struct{ /*頁面控制結構*/ int pn,pfn;struct pfc_struct *next;};typedef struct pfc_struct pfc_type;pfc_type pfc[total_vp],*freepf_head,*busypf_head,*busypf_tail;int diseffect,a[total_instruction];int page[total_instruction],offset[total_instruction];void initialize();void FIFO();void LRU();void OPT();void LFU();void NUR();main(){ int S,i,j;srand(getpid()*10);/*由于每次運行時進程號不同,故可用來作為初始化隨機數隊
列的種子*/ S=(float)319*rand()/32767+1;for(i=0;i
busypf_head=busypf_tail=freepf_head;else {busypf_tail->next=freepf_head;busypf_tail=freepf_head;} freepf_head=p;} } printf(“FIFO:%6.4”,1-(float)diseffect/320);} void LRU(total_pf)/*LRU*/ int total_pf;{ int min,minj,i,j,present_time;initialize(total_pf);present_time=0;for(i=0;i 1、操作系統實驗教程 張麗芬編著 清華大學出版社 2、操作系統原理實驗教程(基于Linux)胡峰松編 清華大學出版社 1.Struts2架構圖和請求處理流程 請求首先通過Filter chain,Filter主要包括ActionContextCleanUp,它主要清理當前線程的ActionContext和Dispatcher;FilterDispatcher主要通過AcionMapper來決定需要調用哪個Action。 ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里創建ActionProxy,ActionProxy創建ActionInvocation,然后ActionInvocation調用Interceptors,執行Action本身,創建Result并返回,當然,如果要在返回之前做些什么,可以實現PreResultListener。 2.Struts2部分類介紹 這部分從Struts2參考文檔中翻譯就可以了。 ActionMapper ActionMapper其實是HttpServletRequest和Action調用請求的一個映射,它屏蔽了Action對于Request等java Servlet類的依賴。Struts2中它的默認實現類是DefaultActionMapper,ActionMapper很大的用處可以根據自己的需要來設計url格式,它自己也有Restful的實現,具體可以參考文檔的docs¥actionmapper.html。 ActionProxy&ActionInvocation Action的一個代理,由ActionProxyFactory創建,它本身不包括Action實例,默認實現DefaultActionProxy是由ActionInvocation持有Action實例。ActionProxy作用是如何取得Action,無論是本地還是遠程。而ActionInvocation的作用是如何執行Action,攔截器的功能就是在ActionInvocation中實現的。 ConfigurationProvider&Configuration ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其實現類XmlConfigurationProvider及其子類StrutsXmlConfigurationProvider來解析。 3.Struts2請求流程 1、客戶端發送請求 2、請求先通過ActionContextCleanUp-->FilterDispatcher 3、FilterDispatcher通過ActionMapper來決定這個Request需要調用哪個Action 4、如果ActionMapper決定調用某個Action,FilterDispatcher把請求的處理交給ActionProxy,這兒已經轉到它的Delegate--Dispatcher來執行 5、ActionProxy根據ActionMapping和ConfigurationManager找到需要調用的Action類 6、ActionProxy創建一個ActionInvocation的實例 7、ActionInvocation調用真正的Action,當然這涉及到相關攔截器的調用 8、Action執行完畢,ActionInvocation創建Result并返回,當然,如果要在返回之前做些什么,可以實現PreResultListener。添加PreResultListener可以在Interceptor中實現。 首先強調一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個線程都有自己獨立的實例變量,互不相干.接下來就從Dispatcher開始看起,先看其構造函數: //創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數 this.initParams = initParams; } //創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數 this.initParams = initParams; } 我們再看在FilterDispatcher創建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接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() 創建Dispatcher之后,來看init()方法 init()方法是用來Load用戶配置文件,資源文件以及默認的配置文件.主要分七步走,看下面注釋 public void init(){ if(configurationManager == null){ //設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構建一個用于依賴注射的Container對象 //在這里面會循環調用上面七個ConfigurationProvider的register方法 //其中的重點就是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){ //設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構建一個用于依賴注射的Container對象 //在這里面會循環調用上面七個ConfigurationProvider的register方法 //其中的重點就是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接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了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實際上只是實現了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構造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在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 //這個props是register的一個入參.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構造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在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 //這個props是register的一個入參.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初始參數值 //如果 沒 有 配 置 就 使 用 默 認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了 //如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可 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負責解析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初始參數值 //如果 沒 有 配 置 就 使 用 默 認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了 //如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可 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負責解析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”); } } } 對于其它配置文件只用接口。 類XmlConfigurationProvider負責配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的 public static Document parse(InputSource inputSource, Map addAction()方法負責讀取 loadInterceptorStack()方法負責將 StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實現ConfigurationProviderloadInterceptorStacks()方法負責將 而上面的方法最終會被addPackage()方法調用,addPackage又會被Provider的loadPackages()調用,將所讀取到的數據匯集到PackageConfig對象中。 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中的內容 private List loadConfigurationFiles(String fileName,Element includeElement){ ...//通過DomHelper調用SAX進行解析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”); //解析每個action配置是,對于include文件可以使用通配符*來進行配置 //如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; } 首先強調一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個線程都有自己獨立的實例變量,互不相干.接下來就從Dispatcher開始看起,先看其構造函數: //創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數 this.initParams = initParams; } //創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param參數 this.initParams = initParams; } 我們再看在FilterDispatcher創建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接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() 創建Dispatcher之后,來看init()方法 init()方法是用來Load用戶配置文件,資源文件以及默認的配置文件.主要分七步走,看下面注釋 public void init(){ if(configurationManager == null){ //設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等 configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構建一個用于依賴注射的Container對象 //在這里面會循環調用上面七個ConfigurationProvider的register方法 //其中的重點就是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){ //設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等 configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //讀取properties信息,默認的default.properties,init_DefaultProperties();// [1] //讀取xml配置文件 init_TraditionalXmlConfigurations();// [2] //讀取用戶自定義的struts.properties init_LegacyStrutsProperties();// [3] //自定義的configProviders init_CustomConfigurationProviders();// [5] //載入FilterDispatcher傳進來的initParams init_FilterInitParameters();// [6] //將配置文件中的bean與具體的類映射 init_AliasStandardObjects();// [7] //構建一個用于依賴注射的Container對象 //在這里面會循環調用上面七個ConfigurationProvider的register方法 //其中的重點就是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接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties() private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了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實際上只是實現了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構造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在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 //這個props是register的一個入參.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構造方法 //讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在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 //這個props是register的一個入參.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初始參數值 //如果 沒 有 配 置 就 使 settings.getImpl(name),用默認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了 //如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可 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負責解析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初始參數值 //如果 沒 有 配 置 就 使 用 默 認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了 //如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可 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負責解析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”); } } } 對于其它配置文件只用接口。 類XmlConfigurationProvider負責配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的 public static Document parse(InputSource inputSource, Map addAction()方法負責讀取 loadInterceptorStack()方法負責將 loadInterceptorStacks()方法負責將 而上面的方法最終會被addPackage()方法調用,addPackage又會被Provider的loadPackages()調用,將所讀取到的數據匯集到PackageConfig對象中。 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中的內容 private List loadConfigurationFiles(String fileName, includeElement){ ...//通過DomHelper調用SAX進行解析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”); //解析每個action配置是,對于include文件可以使用通配符*來進行配置 //如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()調用的是調用的是LegacyPropertiesConfigurationProvider 通過比較前 面 DefaultPropertiesProvider 與 調 用的是LegacyPropertiesConfigurationProvider.發現DefaultPropertiesProvider繼承自后者,但重寫了register()方法,主要是生成PropertiesSetting的不同,前者是根據org/apache/struts2/default.properties 后者是根據struts.properties 我們展開register()中的Settings.getInstance(),最后是調用getDefaultInstance() private static Settings getDefaultInstance(){ if(defaultImpl == null){ // Create bootstrap implementation //不帶參數的DefaultSettings(),區別與DefaultPropertiesProvider中直接帶default.properties參數 //不帶參數就是默認為struts.propertes,并且加載struts.custom.properties所定義的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION為:struts.configuration //在struts.proterties中查找struts.configuration的值,這個值必須是org.apache.struts2.config.Configuration接口的實現類 //所以我有個困惑就是在下面的轉換當中怎么將Configuration轉換成Setting類型的...//這一點先放下了,有時間再研究 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 //不帶參數的DefaultSettings(),區別與DefaultPropertiesProvider中直接帶default.properties參數 //不帶參數就是默認為struts.propertes,并且加載struts.custom.properties所定義的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION為:struts.configuration //在struts.proterties中查找struts.configuration的值,這個值必須是 Could not instantiate the struts.configuration object, substituting the default implementation.”, e); = (Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的實現類 //所以我有個困惑就是在下面的轉換當中怎么將Configuration轉換成Setting類型的...//這一點先放下了,有時間再研究 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);//在這里實現滴~ } }); } //從這里可以看出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);//在這里實現滴~ } }); } 第七步:init_AliasStandardObjects,使用BeanSelectionProvider 這是將配置文件中定義的 接下來是看怎樣調用這些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 { //重點就是這個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 { //重點就是這個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來完成依賴注入的功能 //首先初始化一個ContainerBuilder,再由builder來保存接口與實現類或工廠類的對應關系 //然后通過builder.create(boolean)方法產生container //由container.getInstance(Class);就可以得到接口的實現實例了 //這一部分比較復雜,后面研究完成了,會單獨拿出來講,這里先弄清楚Xwork依賴注入的實現步驟就可以了 ContainerProperties props = new ContainerProperties(); ContainerBuilder builder = new ContainerBuilder(); for(final ContainerProvider containerProvider : providers) { //循環調用ConfigurationProvider的init和register方法,明白了吧,在這里統一循環調用 containerProvider.init(this); containerProvider.register(builder, props); } props.setConstants(builder); //注入依賴關系,在這里并不產生實例 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已經注入依賴關系的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); //調用PackageProvider的loadPackages()方法,這里主要是針對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已經在之前講過,這就好辦了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。 public void init(FilterConfig filterConfig)throws ServletException { try { this.filterConfig = filterConfig; initLogging(); //創建dispatcher,前面都已經講過啰 dispatcher = createDispatcher(filterConfig); dispatcher.init(); //注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader dispatcher.getContainer().inject(this); //StaticContentLoader在BeanSelectionProvider中已經被注入了依賴關系: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(); //創建dispatcher,前面都已經講過啰 dispatcher = createDispatcher(filterConfig); dispatcher.init(); //注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader dispatcher.getContainer().inject(this); //StaticContentLoader在BeanSelectionProvider中已經被注入了依賴關系:DefaultStaticContentLoader //可以在struts-default.xml中的 staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig)); } finally { ActionContext.setContext(null); } } //下面來看DefaultStaticContentLoader的setHostConfig public void setHostConfig(HostConfig filterConfig){ //讀取初始參數 pakages,調用 parse(),解析成類似/org/apache/struts2/static,/template的數組 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){ //讀取初始參數 pakages,調用 parse(),解析成類似/org/apache/struts2/static,/template的數組 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); } 現在回去doFilter的方法,每當有一個Request,都會調用這些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所注入的實現類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進行包裝 //MultiPartRequestWrapper 是 StrutsRequestWrapper的子類,兩者都是HttpServletRequest實現 //此時在MultiPartRequestWrapper中就會把Files給解析出來,用于文件上傳 //所有request都會StrutsRequestWrapper進行包裝,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 { //根據url取得對應的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中 mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); } catch(Exception ex){ log.error(“error getting ActionMapping”, ex); dispatcher.sendError(request,return; } //如果找不到對應的action配置,則直接返回。比如你輸入***.jsp等等 //這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數packages配置的包路徑去查找對應的靜態資源并輸出到頁面流中,當然.class文件除外。如果再沒有則跳轉到 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所注入的實現類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進行包裝 //MultiPartRequestWrapperHttpServletRequest實現 //此時在MultiPartRequestWrapper中就會把Files給解析出來,用于文件上傳 //所有request都會StrutsRequestWrapper進行包裝,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的子類,兩者都是 //根據url取得對應的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中 mapping } catch(Exception ex){ log.error(”error getting ActionMapping“, ex); dispatcher.sendError(request,return; } //如果找不到對應的action配置,則直接返回。比如你輸入***.jsp等等 //這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數packages配置的包路徑去查找對應的靜態資源并輸出到頁面流中,當然.class文件除外。如果再沒有則跳轉到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接口的實現類 DefaultActionMapper的getMapping()方法的源代碼: public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){ ActionMapping mapping = new ActionMapping(); String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時找不到而且的bug uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri; uri = dropExtension(uri, mapping);//刪除擴展名,默認擴展名為action if(uri == null){ return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉重復參數 //如果Action的name沒有解析出來,直接返回 if(mapping.getName()== null){ returnnull; } //下面處理形如testAction!method格式的請求路徑 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接口的實現類 DefaultActionMapper的getMapping()方法的源代碼: public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){ ActionMapping mapping = new ActionMapping(); String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時找不到而且的bug uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri; uri = dropExtension(uri, mapping);//刪除擴展名,默認擴展名為action if(uri == null){ return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉重復參數 //如果Action的name沒有解析出來,直接返回 if(mapping.getName()== null){ returnnull; } //下面處理形如testAction!method格式的請求路徑 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類型的對象,該對象包含三個參數:Action的name、namespace和要調用的方法method。 如果getMapping()方法返回ActionMapping對象為null,則FilterDispatcher認為用戶請求不是Action,自然另當別論,FilterDispatcher會做一件非常有意思的事:如果請求以/struts開頭,會自動查找在web.xml文件中配置的 packages初始化參數,就像下面這樣: org.apache.struts2.dispatcher.FilterDispatcher packages com.lizanhong.action org.apache.struts2.dispatcher.FilterDispatcher packages com.lizanhong.action FilterDispatcher會將com.lizanhong.action包下的文件當作靜態資源處理,即直接在頁面上顯示文件內容,不過會忽略擴展名為class的文件。比如在com.lizanhong.action包下有一個aaa.txt的文本文件,其內容為“中華人民共和國”,訪問 http://localhost:8081/Struts2Demo/struts/aaa.txt時會輸出txt中的內容 FilterDispatcher.findStaticResource()方法 protectedvoid findStaticResource(String name,HttpServletRequest request, HttpServletResponse response)throws IOException { if(!name.endsWith(”.class“)){//忽略class文件 //遍歷packages參數 for(String pathPrefix : pathPrefixes){ InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流 if(is!= null){ ...// set the content-type header String contentType = getContentType(name);//讀取內容類型 if(contentType!= null){ response.setContentType(contentType);//重新設置內容類型 } ...try { //將讀取到的文件流以每次復制4096個字節的方式循環輸出 copy(is, response.getOutputStream()); } finally { is.close(); } return; } } } } protectedvoid findStaticResource(String name,HttpServletRequest request, HttpServletResponse response)throws IOException { if(!name.endsWith(”.class“)){//忽略class文件 //遍歷packages參數 for(String pathPrefix : pathPrefixes){ InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流 if(is!= null){ ...// set the content-type header String contentType = getContentType(name);//讀取內容類型 if(contentType!= null){ response.setContentType(contentType);//重新設置內容類型 } ...try { //將讀取到的文件流以每次復制4096個字節的方式循環輸出 copy(is, response.getOutputStream()); } finally { is.close(); } return; } } } } 如果用戶請求的資源不是以/struts開頭——可能是.jsp文件,也可能是.html文件,則通過過濾器鏈繼續往下傳送,直到到達請求的資源為止。 如果getMapping()方法返回有效的ActionMapping對象,則被認為正在請求某個Action,將調用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關鍵所在。 下面就來看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(); //創建一個Action的代理對象,ActionProxyFactory是創建ActionProxy的工廠 //參考實現類: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,則直接轉向,關于Result,ActionProxy,ActionInvocation下一講中再分析 if(mapping.getResult()!= null){ Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { //執行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了.在這之前最好先去了解一下動態Proxy的基本知識.ActionProxy是Action的一個代理類,也就是說Action的調用是通過ActionProxy實現的,其實就是調用了ActionProxy.execute()方法,而該方法又調用了ActionInvocation.invoke()方法。歸根到底,最后調用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。 最后 通 過 調 用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()這里的步驟是先由ActionProxyFactory創建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); } //創建Action,struts2中每一個Request都會創建一個新的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); //默認為SpringObjectFactory:struts.objectFactory=spring.這里非常巧妙,在struts.properties中可以重寫這個屬性 //在前面BeanSelectionProvider中通過配置文件為ObjectFactory設置實現類 //這里以Spring為例,這里會調到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 會 通 過 web.xml 中的 data fit;input X1 X2 X3 X4 X5 Y1 Y2 Y3;cards;14651000 3446 98.8 2094.51 104.2 2555.14 2637.67 179.76 13985000 3339 113.8 2305.2233 133.8 2462.45 2670.99 161.74 15162900 3093 108.9 2494.6668 93.8 2831.87 3015.04 186 14275800 3084 99.6 2770.48 99.8 2957.2 2259.86 210.3 13966000 3040 101.6 3224.05 142.4 2767.25 2169.47 206.16 13947000 2978 112.4086 3690.34 123.8 2935 2307 218.36 14632000 2952 102.5 3980.44 79.1 3119.91 2332.38 232.33 14123200 2761 106.2 4543.41 97.9 3230.04 2344.04 241.53 14299300 2703 107.3469 5231.33 143.2 3195.12 2411.98 239.79 14849000 2644 111.3 6007.5498 90.8 3342.09 2466.6 252.5 15218000 2604 103.8 6790.899 97.2 3456.7 2471.53 261.34 15344000 2567 99 7565 95.7 3518 2360.31 266.29 run;ods rtf file='F:結果.doc';proccancorr data=fit all vprefix=YING vname='yingxiang' wprefix=CHAN wname='shengchan';var X1 X2 X3 X4 X5;with Y1 Y2 Y3;run;ods rtf close;第四篇:struts2代碼分析
第五篇:典型相關分析SAS代碼