源碼分析DWR的Engine._execute方法

 

DWREngine 作爲dwr客戶端的stub,_execute是其核心方法

正因爲是stub,所以你幾乎不會直接使用這個方法,通常你會在服務器的dwr.xml定義需要暴露的service及其可調用方法,dwr會根據這個配置文件自動生成一個js文件,對應你暴露的service及其方法,你會在本地調用js文件定義的方法,和調用java的方法一樣,除了多了一個回調參數,然後這個方法會委託DWREngine._execute發出請求

/**

* @private Send a request. Called by the Javascript interface stub

* @param path part of URL after the host and before the exec bit without leading or trailing /s

* @param scriptName The class to execute

* @param methodName The method on said class to execute

* @param func The callback function to which any returned data should be passed

*

if this is null, any returned data will be ignored

* @param vararg_params The parameters to pass to the above class

*/

//函數定義

DWREngine._execute = function(path, scriptName, methodName, vararg_params) {

//定義標記,由此標記才能發請求

var singleShot = false;

 

//實例化一個_batch封裝請求

if (DWREngine._batch == null) {

DWREngine.beginBatch();

singleShot = true;

}

// To make them easy to manipulate we copy the arguments into an args array

//定義一個數組存儲遠程方法請求的參數,通常情況下第一個是回調函數,後面纔是與遠程方法對應的//形參

var args = [];

 

//從函數參數的第4個開始算,因爲前3個不算,前3個分別是服務器端的請求路徑,遠程服務腳本名,//遠程服務的方法

for (var i = 0; i < arguments.length - 3; i++) {

args[i] = arguments[i + 3];

}

 

//設置請求路徑

// All the paths MUST be to the same servlet

if (DWREngine._batch.path == null) {

DWREngine._batch.path = path;

}

else {

if (DWREngine._batch.path != path) {

DWREngine._handleError("Can't batch requests to multiple DWR Servlets.");

return;

}

}

// From the other params, work out which is the function (or object with

// call meta-data) and which is the call parameters

 

//定義表量代表純粹的遠程方法的參數

var params;

//定義變量代表本地回調函數

var callData;

//取出請求參數的第一個,通常是回調函數

var firstArg = args[0];

//取出最後一個,通常是遠程方法的參數(一般情況下,除了第一個其他都是)

var lastArg = args[args.length - 1];

 

//1)如果第一個參數函數,那麼進入if

if (typeof firstArg == "function") {

//實例化callData,然後將請求參數數組的第一個賦給callData,並將第一個參數踢出數組

//也就說此時的請求參數數組是純粹的遠程方法的參數數組

callData = { callback:args.shift() };

//將剔除了回調函數的請求參數數組賦給params

params = args;

}

//2)也許你會採用這種格式即最後一個是回調函數,其他是遠程方法參數,那麼下個面的if塊適合你

else if (typeof lastArg == "function") {

callData = { callback:args.pop() };

params = args;

}

//3)如果你將最後一個請求參數定義爲對象且爲其設置了callback方法,那麼那麼下面的if塊適合你

else if (typeof lastArg == "object" && lastArg.callback != null && typeof lastArg.callback == "function") {

callData = args.pop();

params = args;

}

//4)如果第一個參數爲空,那麼進入下面的塊

else if (firstArg == null) {

// This could be a null callback function, but if the last arg is also

// null then we can't tell which is the function unless there are only

// 2 args, in which case we don't care!

 

//如果最後一個參數也爲空,但參數大於2,那麼就會報錯,因爲系統不知道到底那一個纔是//空的回調函數

if (lastArg == null && args.length > 2) {

if (DWREngine._warningHandler) {

DWREngine._warningHandler("Ambiguous nulls at start and end of parameter list. Which is the callback function?");

}

}

callData = { callback:args.shift() };

params = args;

}

//5)第一參數不爲空,最後一個爲空,那麼將最後一個參數之外的所有參數視爲遠程方法參數

else if (lastArg == null) {

callData = { callback:args.pop() };

params = args;

}

//6)以上情況都不成立,拋錯

else {

if (DWREngine._warningHandler) {

DWREngine._warningHandler("Missing callback function or metadata object.");

}

return;

}

 

//通常還是將第一作爲回調函數,後面全部是遠程方法參數,如果無需回調,第一個爲null

//好不容易分開了回調函數很遠程請求參數,繼續前進

 

// Get a unique ID for this call

//產生隨機數作爲請求ID

var random = Math.floor(Math.random() * 10001);

var id = (random + "_" + new Date().getTime()).toString();

//定義一個前綴變量

var prefix = "c" + DWREngine._batch.map.callCount + "-";

//將請求ID存入請求對象的ID數組

DWREngine._batch.ids.push(id);

 

//如果你是將一個對象而非函數作爲回調函數,見上面的第3種情況,那麼一下一段代碼適合你,否則//跳過

// batchMetaData stuff the we allow in callMetaData for convenience

 

//設置_batch的method屬性

if (callData.method != null) {

DWREngine._batch.method = callData.method;

delete callData.method;

}

 

//設置_batch的verb 屬性

if (callData.verb != null) {

DWREngine._batch.verb = callData.verb;

delete callData.verb;

}

//設置_batch的async 屬性

if (callData.async != null) {

DWREngine._batch.async = callData.async;

delete callData.async;

}

////設置_batch的timeout 屬性

if (callData.timeout != null) {

DWREngine._batch.timeout = callData.timeout;

delete callData.timeout;

}

//如果callData存在preHook ,那麼將此preHook 加入_batch的preHook數組的第一個

// callMetaData stuff that we handle with the rest of the batchMetaData

if (callData.preHook != null) {

DWREngine._batch.preHooks.unshift(callData.preHook);

delete callData.preHook;

}

//如果callData存在postHook ,那麼將此preHook 加入_batch的postHooks數組的最後一個

if (callData.postHook != null) {

DWREngine._batch.postHooks.push(callData.postHook);

delete callData.postHook;

}

 

// Default the error and warning handlers

//設置錯誤處理器和警告處理器

if (callData.errorHandler == null) callData.errorHandler = DWREngine._errorHandler;

if (callData.warningHandler == null) callData.warningHandler = DWREngine._warningHandler;

 

// Save the callMetaData

//將回調函數放入_handlersMap對象中,以id作爲key或屬性

DWREngine._handlersMap[id] = callData;

 

//設置_batch的map對象

    //設置腳本屬性

DWREngine._batch.map[prefix + "scriptName"] = scriptName;

    //設置方法屬性

DWREngine._batch.map[prefix + "methodName"] = methodName;

    //設置id屬性

DWREngine._batch.map[prefix + "id"] = id;

 

// Serialize the parameters into batch.map

//爲DWREngine添加編碼函數

DWREngine._addSerializeFunctions();

 

//將遠程方法參數編碼爲某種格式

//以下是幾種格式的例子

//比如一個一個js字符串的值爲323YN2O2,那麼編碼後成爲 "string:323YN2O2"

//比如一個對象編碼爲"Object:{tvUserId:reference:c0-e6, userNo:reference:c0-e7, loginName:reference:c0-e8}"


//編碼全部存入_batch的map對象

for (i = 0; i < params.length; i++) {

DWREngine._serializeAll(DWREngine._batch, [], params[i], prefix + "param" + i);

}

 

//刪除DWREngine剛剛添加的編碼函數

DWREngine._removeSerializeFunctions();

 

// Now we have finished remembering the call, we incr the call count

//調用此數加1

DWREngine._batch.map.callCount++;

//標記爲true,發送請求

if (singleShot) {

DWREngine.endBatch();

}

};

 

總結:_execute其實只幹了兩件事情

1)分離回調函數和遠程方法參數

2)編碼遠程方法參數

 

最後再調用另一個很重要的函數endBatch(其實是_sendData)來完成請求

 

從_execute方法可以看出DWREngine有個很重要的數據結構就是_batch

_batch可以視爲請求參數的封裝器,其實比這更多

它包含以下幾個屬性

    ids (字符數組),map(對象)(主要包含c0-id.c0-methodName,c0-scriptName,callCount,c0-paramx(x012,有幾個參數就有幾個paramx ),還包含很多細小的參數對象的屬性編碼,每個編碼都爲一個屬性),paramCount(數值),path(字符串),preHooks(函數數組),postHooks(函數數組)

發佈了22 篇原創文章 · 獲贊 10 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章