您的位置:首页 > 其它

源码分析DWR的Engine._execute方法

2011-09-22 16:57 337 查看
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(x为0或1或2,有几个参数就有几个paramx
),还包含很多细小的参数对象的属性编码,每个编码都为一个属性),paramCount(数值),path(字符串),preHooks(函数数组),postHooks(函数数组)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: