struts ModuleConfig類加載初始化的過程以及RequestProcessor類源碼分析
Posted on 2009-04-07 15:33 鄭舒力 閱讀(1050) 評論(0) 編輯 收藏 所屬分類: struts 學習筆記簡要的對struts ActionServlet init()方法以及 struts RequestProcessor類做源碼分析
首先要確定的是Servlet至始至終只有一個對象以及init()方法會在Servlet第一次加載時被執行一次也是唯
一的一次,所以Servlet的初始化工作一般在init()方法進行
一、解析ModuleConfig
ModuleConfig封裝了struts-config的所有配置信息
actionConfigs(ActionMapping)/actionConfigList 、exceptions 、formBeans(ActionForm)、forwards
(ActionForward)、messageResources、plugIns等
// 初始化ModuleConfig配置工廠
initModuleConfigFactory();
// 由配置工廠實例化一個ModuleConfig的對象
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleConfig(String prefix, String paths)方法做了一下事情
1、 ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
由生成的配置工廠生成一個工廠實例
2、ModuleConfig config = factoryObject.createModuleConfig(prefix);
創建ModuleConfig實例createModuleConfig()方法會有DefaultModuleConfigFactory 執行
這是在配置文件中指定的工廠類
方法內部new 出ModuleConfig對象
在ModuleConfig構造方法內對以下參數進行了初始化














到此config構造完成
Digester digester = initConfigDigester();
initConfigDigester()方法添加解析struts文件的解析規則
3、循環struts配置文件并把所有標簽封裝為相應對象填充到config對象相應的map集合中中
List urls = splitAndResolvePaths(paths);
URL url;
for (Iterator i = urls.iterator(); i.hasNext();) {
url = (URL) i.next();
digester.push(config);
this.parseModuleConfigFile(digester, url);
}
最后getServletContext().setAttribute(Globals.MODULE_KEY
+ config.getPrefix(), config);
把ModuleConfig設入ServletContext中
至此config初始化工作全部完成
struts每個模塊都對應一個ModuleConfig對象個,在init方法內被初始化,內部封裝了模塊xml文
件中的配置
二、struts核心類RequestProcessor類解析
ActionServlet做為前端控制器當有請求被接收時,會調用process(request, response)方法
1、ModuleConfig config = getModuleConfig(request);
通過Servlet上下文找到ModuleConfig對象
2、RequestProcessor processor = getProcessorForModule(config);
通過Servlet上下文找到RequestProcessor對象 --所以RequestProcessor類也是單例的
if (processor == null) {
processor = getRequestProcessor(config);
}
如果為空 也就說明是服務器端第一次接收客戶端的接收請求
那么執行getRequestProcessor(config)方法生成RequestProcessor并設入ServletContext里
此方法為同步方法,借此我猜測此方法為生成RequestProcessor的唯一方法
方法內部對RequestProcessor調用了init()方法,進行了RequestProcessor的初始化并設入ServletContext
里。
為什么RequestProcessor要做成單例,原因就是RequestProcessor類內部有
HashMap actions = new HashMap();
這樣一個集合封裝了所有的控制器。
這樣的設計是面向對象的。當ActionServlet接收到請求時它需要對請求分發到相應控制器中
此時它獲取中央控制器,而這個中央控制器內部擁有所有控制器的引用。
/*
這里我的疑惑是為什么要通過反射生成RequestProcessor,此類并沒有繼承實現任何接口或類。
process方法最后調用了RequestProcessor的process()方法。此方法為RequestProcessor的核心方法
*/原因經過學習發現RequestProcessor并不是單例的,而實際因為struts的多模塊應用模式,導致
RequestProcessor類是多例的
三、解析RequestProcessor類核心方法process
1、 String path = processPath(request, response);
截取客戶端請求字符串相當于<action>標簽的path屬性
2、ActionMapping mapping = processMapping(request, response, path);
獲取控制器相對的ActionMapping 對象
processMapping(request, response, path)
1、ActionMapping mapping =
(ActionMapping) moduleConfig.findActionConfig(path);
方法內部調用了moduleConfig.findActionConfig(path);
ModuleConfig對象前面已做過解析,在findActionConfig方法內部已path屬性做為Key值,直接
到ActionConfigHashMap集合內尋找ActionMapping,原因見一.3
2.if (mapping != null) {
request.setAttribute(Globals.MAPPING_KEY, mapping);
return (mapping);
}
如果找到mapping那么放入request中,并返回
3、 ActionConfig[] configs = moduleConfig.findActionConfigs();
for (int i = 0; i < configs.length; i++) {
if (configs[i].getUnknown()) {
mapping = (ActionMapping) configs[i];
request.setAttribute(Globals.MAPPING_KEY, mapping);
return (mapping);
}
}
//ActionMapping說明,和Action的unknow屬性有關
3、if (mapping == null) {
return;
}
容錯處理,說明客戶端請求的path路徑并沒有被配置
4、ActionForm form = processActionForm(request, response, mapping);
processActionForm(request, response, mapping);
1、ActionForm instance =
RequestUtils.createActionForm(request, mapping, moduleConfig,
servlet);
1、String name = mapping.getName();
獲得mapping name屬性也就是映射的相對ActionForm的name
2、FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
已name做為Key值在緩存的HashMap內獲取相對(FormBeanConfig)對象
<!--對這里的一些繼承關系做下說明
struts Config類的基類 BaseConfig
ForwardConfig從BaseConfig繼承
FormBeanConfig從BaseConfig繼承
ActionConfig從BaseConfig繼承
ActionMapping從ActionConfig繼承
-->
3、if (config == null) {
log.warn("No FormBeanConfig found under '" + name + "'");
return (null);
}
如果為空也就說明并沒有配置相對ActionForm,而這是合法的所以返回空
4、ActionForm instance =
lookupActionForm(request, attribute, mapping.getScope());
此方法試圖在request,session內尋找ActionForm
5、if ((instance != null) && config.canReuse(instance)) {
return (instance);
}
如果找到那么返回
6、return createActionForm(config, servlet);
如果沒有找到則進行創建,方法傳遞了config,因為config內封裝了formbean標簽的所
有配置信息。方法可以根據類名進行反射生成對象。
2、if ("request".equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), instance);
} else {
HttpSession session = request.getSession();
session.setAttribute(mapping.getAttribute(), instance);
}
根據配置信息,把ActionForm設入相應作用域內
此方法作用就是獲取ActionForm
5、processPopulate(request, response, form, mapping);
此方法用于做ActionForm的數據收集工作
1、form.reset(mapping, request);
此方法用于做數據重置,程序員可以重寫此方法
2、RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
request);
調用工具類進行數據收集
1、if ((contentType != null)
&& (contentType.startsWith("multipart/form-data"))
&& (method.equalsIgnoreCase("POST"))) {
判斷是否是數據上傳
2、if (!isMultipart) {
names = request.getParameterNames();
}
如果不是獲取request里所有參數參數名。
3、 while (names.hasMoreElements()) {
String name = (String) names.nextElement(); //循環獲取參數名
String stripped = name;
Object parameterValue = null;
...
if (isMultipart) {
parameterValue = multipartParameters.get(name);
parameterValue = rationalizeMultipleFileProperty(bean, name,
parameterValue);
} else { //不是上傳 則獲取此參數名的所有參數值
parameterValue = request.getParameterValues(name);
}
if (!(stripped.startsWith("org.apache.struts."))) {
properties.put(stripped, parameterValue);
}
}
方法最后把參數名做為Key,所對應的所有參數值做為鍵值放入hashmap內
5、BeanUtils.populate(bean, properties);
由第三方工具類根據ActionForm以及所有參數名參數值進行數據收集
3、if (!processValidate(request, response, form, mapping)) {
return;
}
進行數據驗證,方法內部會調用ActionForm的validate方法,程序員可以重寫此方法
6、Action action = processActionCreate(request, response, mapping);
獲取控制器
1、方法首先獲取Action的類名
2、然后此方法對actions進行了同步處理,原因RequestProcessor是多例的,actions是一個成
員變量,緩存了所有action。
3、instance = (Action) actions.get(className);
if (instance != null) {
if (log.isTraceEnabled()) {
log.trace(" Returning existing Action instance");
}
return (instance);
}
試圖在集合內取出action,如果不為空 則返回。此時說明此Action已被執行過。
4、instance = (Action) RequestUtils.applicationInstance(className);
如果沒有反射創建Action引用。
5、actions.put(className, instance);
最后把action放入集合,并返回。
7、ActionForward forward =
processActionPerform(request, response, action, form, mapping);
此方法內部調用Action的execte方法,獲取轉向類。
<!--說明類組織關系
在他的父類里有 HashMap forwards = new HashMap();這樣一個集合
這里的類組織關系是這樣的,ActionMapping從ActionConfig繼承,
ActionConfig擁有forwards 集合,
而ModuleConfig內部擁有ActionConfig集合,
這和struts-config標簽的組織關系是完全類似的.
ModuleConfig模塊配置類對應一個struts配置文件,
ActionConfig控制器配置類對應一個Action標簽,
ForwardConfig轉向配置類對應Forward標簽,
而ModuleConfig在ActionServlet初始化時被加載。
這里可以看出大師對類的組織的合理,每個標簽都有相應的類。
-->
綜上所述,由于forward標簽屬于ActionMapping標簽所以在程序里,能用findForward()方法找到
ActionForward.
而在ActionConfig內部有這樣一個方法addForwardConfig(ForwardConfig config);它已ForwardConfig 的
name屬性做為Key,ForwardConfig 類實例做為值添加入緩存中。
這也就是為什么我們可以在findForward()方法內通過傳name屬性獲取相應的ActionForward實例。
8、processForwardConfig(request, response, forward);
在process方法的最后調用了processForwardConfig()方法。完成轉向操作。
在這個方法內部,他根據Redirect()是否設置為true選擇是進行轉發還是重定向。如果沒有設置,默認是進
行轉發。
說明:
此篇學習筆記算是閱讀尚學堂王勇老師struts視頻的讀后感吧,當時小弟剛學習struts,也算是個j2ee的新人就斗膽寫了這篇源碼的分析,今天發上來并未做任何刪減,錯誤是難免的,還希望各位大蝦閱讀時,多多指正,不吝賜教!!
最后感謝由衷的感謝王勇老師,感謝尚學堂!!