DWR整体流程分析
(一)三个基本的准则 对于理解ajax理解dwr有一定的帮助
1、浏览器:应用而非内容(非显示)
我们在以往的web编程中,浏览器仅仅是为了显示页面而存在,但是现在有了ajax,浏览器不单单就是为了显示,为什么这么说呢?因为以往的web编程中,浏览器是把服务器发送给浏览器的这个页面给显示出来,仅仅完成这样的一些工作,它其中也嵌入一些javascript的脚本,但是这时候的脚本仅仅就是为了让页面显示得更加美观而设计的没有其他的用处,但是有了ajax之后呢,浏览器当中ajax的javascript脚本不单单就是为了美化这些页面而存在,它还有一部分作用就是对服务器端传过来的数据我需要进行分析,需要要进行处理,然后才能显示在浏览器端。大家都知道,ajax从服务器端传过来的数据有可能是response.text或者是response.xml,如果是text的话,也许就可能在浏览器中直接显示,但是如果是xml的文件的话,浏览器会先把这个xml文件解析之后才显示出来。这个解析的过程也就是相当于是一种应用,而非内容的显示了,所以说浏览器方不仅仅是内容了,应该是一种应用。所以是应用而非内容。
2、服务器:数据而非内容
以往服务器端仅仅是发送给浏览器端它需要的页面,这个页面也就是内容,但是现在服务器端不需要发送一些重复性的数据了,这个重复性的数据也就是相当于是我们编写的页面中的页头页角框架等等一些重复性的。它们在服务器端和客户端之间传输,占用一定的网络带宽,传输的数据量变大,速度也会下降的。所以在ajax当中,我们传输到服务器端进行处理的东西仅仅是页面当中的一小块,而服务器端返回的仅仅是我们需要的那一部分数据,然后在浏览器端通过它的应用,通过它的设置来显示我们所要的东西,所以说服务器端是发送给浏览器的数据,而不是内容了。
3、编程变得严谨
因为我们在以往的浏览器当中呢,我们仅仅是通过js的脚本来进行一些页面的美观,使页面便得跟花哨一些,但是现在浏览器端它处理的内容将是对数据的处理,它仅仅是一种应用,所以说对javascript的脚本来说,这种弱类型的语言,它的编程就需要更加的严谨。不单单是为了显示在页面上而已。对于我们学java这种强类型的语言来说呢,对这种弱类型的语言的学习有一定的困难。也就是很不适应。要对javascript脚本的学习要有一种耐心细心的精神。
(二)DWR的请求处理
当页面发出请求,Servlet被调用是在什么时机?
我们先来建一个名叫TestSerlet的web工程
看看我们的Servlet这么写的:
package com.lukuijun.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request,response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("请求被调用");
}
}
接着我们配置一下web.xml的部署文件:
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.lukuijun.demo.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/lukuijun/*</url-pattern>
</servlet-mapping>
注意:这个url-pattern的格式你是可以随便写的,格式由你自己定,但是在访问的时候也要按照你写的这个格式来的。
接着我们在index.jsp里面引入script资源:
<script type="text/javascript" src="/TestServlet/lukuijun/lukuijun.js"></script>
也就是我在加载index.jsp这个页面,将这个script资源引入,而引入的这个资源lukuijun.js并不存在。但是它引用的路径src是我们这个应用TestServlet中的lukuijun/lukuijun.js,也是是我们在web.xml里面配置的<url-pattern>/lukuijun/*</url-pattern>的请求路径。
说明一点:如果采用在Tomcat里配置context的虚拟路进的话,如果把context的path配置为"",即是path="",那么在配置script的src的时候就不需要应用名TestServlet,总之一句话,/lukuijun/lukuijun.js前面的应用名和context的path属性一样,而且必须以"/"打头。
接着我们在Tomcat里面发布运行一下看看结果:
在控制台里面打出一句话:请求被调用
这里我们看看当我请求index.jsp这个Servlet会执行那些操作,当我们请求index.jsp时,接着就调用servlet打印出"请求被调用"。
一般来说发给servlet的请求呢一般都是在一个页面,假如说在index.jsp这个页面,在这个页面里呢,我们是调用一些链接,这个链接里面包括一些请求的路径,可能是/lukuijun/这个路径,有可能是form的action来发送的请求到它处理。但是我们容易忽略一点:就是在这个页面请求加载的时候,它需要引入一些资源,引入的资源包括文本文件、脚本文件等等。把它们引入的时候呢,如果路径也是servlet可以处理的,那么它会先处理引用资源的这个路径的文件。这里呢就是我在加载这个index.jsp文件的时候,我引入src对应的资源。但是这个资源呢实际上是不存在的,也就是没有这个资源,但是我注意到机器是靠我的指令来执行的,我说这个servlet要对这个请求格式的资源进行处理,所以我把这个src资源路径写成了我刚才创建的TestServlet可以处理的路径。所以我引入这个资源的时候会去调用这个servlet,然后这个servelt会打印输出这样一句话:请求被调用。
讲此目的:不要忽略一点,因为往往都会在主页面当中发送请求对其处理,但是对于我所说的在页面加载index.jsp的时候,srcipt引入的src这些资源也会被servlet进行处理,这一点一定要注意,而且这一点在dwr里面也有所体现。正式有了它们的引用,有了它们的前处理,才使得我们有些内容,脚本文件,得以顺利的执行。
好,现在来看看工程Demo1:
1、index.jsp引入了三个javascript资源
<script type="text/javascript" src="/Demo1/dwr/engine.js"></script>
<script type="text/javascript" src="/Demo1/dwr/util.js"></script>
<script type="text/javascript" src="/Demo1/dwr/Demo.js"></script>
engine.js是引擎
util.js是工具类
Demo.js是我们配置的,我们需要在远程调用的java方法,这个java类会通过服务器端的dwr框架的一些内容,返回给客户端的一个脚本文件。这些东西,前两个可以说是有型的,在dwr.jar下我们会看见有这么两个文件engine.js和util.js。所以引入它们很正常。但是路径src却是servlet请求路径。所以在引入这些资源的时候,都需要在servlet中进行处理。引入engine.js需要到servlet进行处理,处理的时候都做些什么?它主要是对这个文件当中的一些页面的id进行处理,它里面有一些变量呢需要在服务器端发送给客户端,在客户端注册进来。引入engine.js,servlet基本上不做任何处理,因为其中的内容仅仅是一些工具的方法,来让我们在客户端调用使用的,比较方便。引入Demo.js,需要在客户端根据dwr的配置文件dwr.xml所指定的信息来生成我们所需要的一个脚本文件。这个脚本文件就是我们客户端要调用它来完成对服务器端方法的调用。
明白一点:对客户端发送的请求,也许这个请求是页面发过去,也许是通过javascript的src引入的资源,不管哪种形式,最终都要通过servlet来进行处理。前提是它们请求的资源
的路径要满足设置的servlet所请求的路径(url-pattern)。DWR对请求的处理一定不能忽略,否则不可能对dwr有深入理解。
(三)服务器的脚本生成
在Demo1工程中,服务器脚本的生成是通过engine,js,并不是生成engine.js,而是为engine.js里面的内容进行赋值。看一下engine.js这个文件:
** The original page id sent from the server */
dwr.engine._origScriptSessionId = "${scriptSessionId}";
/** The session cookie name */
dwr.engine._sessionCookieName = "${sessionCookieName}"; // JSESSIONID
/** Is GET enabled for the benefit of Safari? */
dwr.engine._allowGetForSafariButMakeForgeryEasier = "${allowGetForSafariButMakeForgeryEasier}";
/** The script prefix to strip in the case of scriptTagProtection. */
dwr.engine._scriptTagProtection = "${scriptTagProtection}";
/** The default path to the DWR servlet */
dwr.engine._defaultPath = "${defaultPath}";
/** Do we use XHR for reverse ajax because we are not streaming? */
dwr.engine._pollWithXhr = "${pollWithXhr}";
/** The read page id that we calculate */
dwr.engine._scriptSessionId = null;
原始的page id是从服务器端发送过来的,什么意思呢?这些内容是从服务器端发送过来的,这个文件是放在客户端的,但是它从服务端发送过来,它是以什么形式发送过来?发送过来以后是怎么给它们赋值的?这个就设计到dwr源码的问题。以后会讲解,今天明白一点:我在这个引擎脚本用的时候,在之前,我必须要对这些变量进行赋值,否则,dwr框架就无法应用。但是赋值的时机在哪儿?在什么情况下为它赋值,这就是dwr请求处理当中的引入的资源文件。engine.js这个资源文件是已经存在的,但是为什么还要引入它?而且路径还必须写成servlet的请求处理路径。目的就是为了给文件里面的这些变量进行初始化。为了验证下,我们可以把这些变量中有用的值打印出来。
打印原始的ScriptSessionId:dwr.engine._origScriptSessionId,这个在index.jsp的什么地方写呀?因为这个页面加载的时候,会先去处理engine.js这个脚本引擎文件,通过它的处理,会把值赋值给engne.js里面的变量。所以目前状况只要这个页面加载,这些变量就已经赋值了。所以在engine.js脚本引擎加载后就可以对其中的变量获得的值进行打印。
在加载engine.js后,执行下面一个函数
function show(){
alert("origScriptSessionId:" + dwr.engine._origScriptSessionId);
}
打印出origScriptSessionId:DFCB69D86F4E1AC9CF3900A68164E40A,这个就是服务器端发送给浏览器端的出示的ScriptSessionId,我记住它的后四位E40A,然后在刷新一下页面,重新提交一下打印出origScriptSessionId:DFCB69D86F4E1AC9CF200A68164E3CA,现在后四位E3CA这个和原来的不一样了。造成不一样的原因是什么呢?还是在engine.js,在页面请求加载的时候,它会先去处理engine.js文件,当刷新的时候实际上是又对服务器发送了一次请求,服务器又会给我们返回engine.js里面变量的值赋给它们,每一次请求这些变量的值都是不一样的。如果不刷新重复提交,origScriptSessionId给我们赋值后,它的值不刷新是不会变的。出发再请求了servlet路径下的engine.js文件,它才会给我们重新赋值。
服务器端脚本生成,第一个就是引擎脚本文件engine.js在加载后,服务器会为我们生成一些变量的值。看一下变量dwr.engine._origScriptSessionId,它是原始的scriptSessionId,它和httpSessionId有所区别:因为httpSessionId它存储在客户端,存在客户端的作用就是当客户端发出请求的时候,它会把这个httpSessionId发送出去,发送出去过后,服务器端端有一个session,它会通过这个session id来判断这个用户是属于哪个session里的,然后到session里去取它的用户信息。如果这个session id不存在,服务器就会为这个客户端创建一个,然后发送给给客户端保存。然后客户端下次再访问的时候,就会把这个id发送给服务器端了,这个session是保存在服务器端的。但是现在这里的原始的scriptSessionId是保存在客户端的。就是客户端每发送一个请求,服务器端都会产生一个scriptSessionId,这个scriptSessionId是针对页面来讲的。在源码中,会详细介绍这一部分。这一部分也是很重要的,因为它提出了新的session,而不是我们原来的http session。它是脚本方面的一个session并且存储在客户端.注意区别我们平时的http
session。
引擎当中还有一个session:dwr.engine._getJSessionId,它这个dwr.engine._getJSessionId是一个方法:
/** @private What is our session id? */
dwr.engine._getJSessionId = function() {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) {
return cookie.substring(dwr.engine._sessionCookieName.length + 1, cookie.length);
}
}
return "";
};
这个dwr.engine._getJSessionId是一个方法,就相当于httpSessionId。dwr.engine._getJSessionId的作用也是存储在客户端,但是他会产生一个id,这个id的产生方式就是通过这个引擎脚本当中的方法来产生的。看看它的实现:首先它会从浏览器方获得所有的cookie,这些cookie是以分号分割的。然后取出每一个cookie。cookie是以键值对的形式存在的。前面是键然后=后面是值。我们要getJSessionId,前面就应该是一个键,以key=id形式存在的。这里先取出所以cookie,然后循环每一个cookie,判断这个cookie的第0个字符是不是空的,如果是空的话就从第一个位置开始取。因为前面有很多空格,cookie要忽略它的空格。所以有cookie.charAt(0) == ' '判断。这么循环下来之后呢,把前面所有的空格都会消除掉了,最后的形式就是key=value这么一种,而不是前面是一大堆的空格,去掉所有空格后,在判断cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0如果cookie是以dwr.engine._sessionCookieName=开头的话,他就会返回=后面的值,也就是那个SessionId。
看一下sessionCookieName:
/** The session cookie name */
dwr.engine._sessionCookieName = "${sessionCookieName}"; // JSESSION
ID
The session cookie name它也是服务器端返回给客户端的,所以说服务器端采用什么方式获得了这个sessionCookieName的形式之后,我们的dwr.engine._getJSessionId才能根据它的内容它的形式来返回我们想要的cookie,这个实现方式就是dwr所依赖的实现方式。如果dwr实现了这个cookieName是别的名字,那我们也需要dwr给返回别的名字。dwr.engine._sessionCookieName 这个名字是因框架而定。所以它会采用这样一种写法。
我们看看打印
function show(){
alert("sessionCookieName:" + dwr.engine._sessionCookieName);
}
打印出sessionCookieName:JSESSIONID
那么我们现在取得的JSESSIONID就是JSESSIONID=我们所要的字符串,我们验证一下,用下面的代码:
var cookies = document.cookie.split(';');
alert(cookies );
打印出JSESSIONID=FCB69D86F4E1AC9CF3900A6816。它和origScriptSessionId是不一样的。我们来看看httpSessionId(JSESSIONID),它应该是对一个客户的所有请求来讲的。会话,对多个请求只要是同一个客户都可以,因此对于同一个客户它的httpSessionId是不会变的。我们来看对同一个客户第一次请求JSESSIONID=FCB69D86F4E1AC9CF3900A6816,刷新后第二次请求JSESSIONID=FCB69D86F4E1AC9CF3900A6816,它们俩次显示的JSESSIONID是相同的。因为它们都是相当于同一会话来讲的。这个内容也就是存储在客户端,客户端发送数据的时候也要把它们发送出去,发送出去到服务器端后,服务器端判断这个SESSIONID存不存在。如果存在的话,就会根据它去取出我们所要的session,然后把当前客户这些状态取出来显示出来。
我们看一下原始的脚本ScriptSessionId,我们知道它的产生是服务器每一次进行加载页面,页面中包含了engine.js这个资源的时候,我们会把原始的ScriptSessionId赋值,所以我们每一次的请求都会产生一个不同的ScriptSessionId。所以脚本的原始的ScriptSessionId,针对每一次的不同请求,它都会产生不同的原始的ScriptSessionId。
(四)execute方法
下面我们看一下脚本文件的生成:
Demo.js:
//Provide a default path to dwr.engine
if(dwr == null) var dwr = {};
if(dwr.engine == null) dwr.engine = {};
if(DWREngine == null) var DWREngine = dwr.engine;
if(Demo == null) var Demo = {};
Demo._path = '/Demo1/dwr';
Demo.sayHello = function(p0,callback){
dwr.engine._execute(Demo._path,'Demo','sayHello',p0,callback);
}
这是服务器端为我们产生的脚本文件,正因为有了这个脚本文件,客户端才可以通过Demo.sayHello来访问到服务器组件。到底是怎么访问的?大家知道,dwr是基于ajax产生的,所以它必然会用到ajax的xmlHttpRequest对象,被dwr封装了,包括servlet在ajax中也需要我们来写,但是在dwr中也封装来了,只要在web.xml配置org.directwebremoting.servlet.DwrServlet就可以完成我们的处理了。这也是dwr优于ajax的一点,框架的集成度是比较高的。
看一下Demo.sayHello,它是通过dwr.xml这个部署配置文件来动态产生的, dwr.engine._execute(Demo._path,'Demo','sayHello',p0,callback);这些东西是服务器发我们的,这是dwr的一个内部实现机制。看一下调用Demo.sayHello方法的时候,具体的操作是什么?它首先去调用函数function(p0,callback),说白了,客户端直接调用web服务器的组件呢,实际说到最终的实质也就是服务器端为我们产生一个js脚本文件,产生的这个文件是根据我们的dwr.xml部署描述文件动态生成的,生成之后呢,我们客户端就调用生成的这个文件当中的信息,然后通过这个信息再去调用engine.js文件当中的execute方法,然后把必要的参数传递进去之后,在dwr.engine._execute(Demo._path,'Demo','sayHello',p0,callback);里面完成对象的创建,包括最后的发送,最后的相应等等。所以这个引擎的脚本文件是很重要的。下面分析一下引擎脚本文件中的
dwr.engine._execute方法:
dwr.engine._execute = function(path, scriptName, methodName, vararg_params)
引擎文件的执行方法,需要4个参数,注意一点,看源码的时候, javascript定义一个函数的时候,后面的参数并不是我们在dwr.engine._execute(Demo._path,'Demo','sayHello',p0,callback);里面指定的,因为这是5个参数,但前面引擎文件的执行方法定义时却只有4个参数。这个和强类型语言有所区别,强类型语言对于函数重载方面,它参数不同,它会去调用不同的方法。但是这里如果仅仅有一个函数的话,这个函数的参数不同,它也会去调用。所以不能从参数的数量上来确定调用哪一个函数。vararg_params相当于是一个数组,相当于有很多的参数在里面,这一点一定要注意。所以客户端在调用了Demo.sayHello方法之后,会执行引擎脚本的execute方法,然后会传递几个参数Demo._path,'Demo','sayHello',p0,callback。Demo._path是在服务器端动态的生成的,它也就是指定我们请求的路径是什么。请求都是通过servlet来处理的,请求路径应该就是servlet所需要的路径。Demo指的是dwr.xml文件当中,为我们指定的javascript的属性所指定的在客户端调用的对象的方法的对象的名称,sayHello就是指定我所调用的是哪一个方法,p0就是指定调用的参数是什么(就是本例的name),callback就是回调方法。这就是我们在客户端在书写格式上要采用Demo.sayHello(text,callBack),前面text指的是参数,后面指定的是回调函数callBack。这也是和实现机制有关的,否则我们这么写为什么会去调用它,我们不得而知,但是当你看到了这个生成的引擎文件的时候,我就知道,实际我调用sayHello方法之后,传递的两个参数function(p0,callback),p0是我们传递的参数,callback是回调方法,然后吧这些信息是通过引擎文件的execute方法去执行的。但是传递这个方法的时候,会传递另外一些附加的信息。而且回调函数传过去,传过去之后在引擎中,会对数据进行分析,分析之后回传的时候会去调用callback,然后就会调用我们在index.jsp里面写的callBack(data)方法显示出数据。这就是整体流程。
下面分析引擎文件的执行方法,看源码:
dwr.engine._execute = function(path, scriptName, methodName, vararg_params){
var singleShot = false;
if (dwr.engine._batch == null) {
dwr.engine.beginBatch();
singleShot = true;
}
var batch = dwr.engine._batch;
// To make them easy to manipulate we copy the arguments into an args array
var args = [];
for (var i = 0; i < arguments.length - 3; i++) {
args[i] = arguments[i + 3];
}
// All the paths MUST be to the same servlet
if (batch.path == null) {
batch.path = path;
}
else {
if (batch.path != path) {
dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"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 callData;
var lastArg = args[args.length - 1];
if (typeof lastArg == "function" || lastArg == null) callData = { callback:args.pop() };
else callData = args.pop();
// Merge from the callData into the batch
dwr.engine._mergeBatch(batch, callData);
batch.handlers[batch.map.callCount] = {
exceptionHandler:callData.exceptionHandler,
callback:callData.callback
};
// Copy to the map the things that need serializing
var prefix = "c" + batch.map.callCount + "-";
batch.map[prefix + "scriptName"] = scriptName;
batch.map[prefix + "methodName"] = methodName;
batch.map[prefix + "id"] = batch.map.callCount;
for (i = 0; i < args.length; i++) {
dwr.engine._serializeAll(batch, [], args[i], prefix + "param" + i);
}
// Now we have finished remembering the call, we incr the call count
batch.map.callCount++;
if (singleShot) dwr.engine.endBatch();
};
path是请求的路径,scriptName是客户端所用的java对象的名称methodName是调用的方法名 vararg_params是方法的参数,这里面的参数是包括了客户端发送给服务器端需要的java方法的参数名称,还有一个就是回调函数。
首先看看dwr.engine.beginBatch();开始一个批处理,目的是因为dwr框架在开发的时候遵循的一种方式就是说,如果多次调用java的web组件的话,我需要放在一个批处理当中来处理,这样会提高效率,避免了客户端和服务器端频繁的发生交互。所以需要一个批处理。但是默认情况下,都会采用批处理,它会生成一个Batch的批处理对象。看一下dwr.engine.beginBatch源码:
dwr.engine.beginBatch = function() {
if (dwr.engine._batch) {
dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
return;
}
dwr.engine._batch = dwr.engine._createBatch();
};
它也是一个函数,目的就是为了生成一个Batch批处理的对象。生成这个Bactch对象的目的是什么?也就是完成我所需要的操作,操作中的数据都是放在Bactch对象里的,然后通过Bactch来操作,beginBatch需要做的是什么呢?就是如果这个Bactch对象存在的话,就会抛出错误已经存在了,不需要创建,然后直接返回。dwr.engine._batch =
dwr.engine._createBatch();Batch的创建是通过createBatch()来创建的,createBatch()也是一个函数,看看它的源码:
/** @private Generate a new standard batch */
dwr.engine._createBatch = function() {
var batch = {
map:{
callCount:0,
page:window.location.pathname + window.location.search,
httpSessionId:dwr.engine._getJSessionId(),
scriptSessionId:dwr.engine._getScriptSessionId()
},
charsProcessed:0, paramCount:0,
parameters:{}, headers:{},
isPoll:false, handlers:{}, preHooks:[], postHooks:[],
rpcType:dwr.engine._rpcType,
httpMethod:dwr.engine._httpMethod,
async:dwr.engine._async,
timeout:dwr.engine._timeout,
errorHandler:dwr.engine._errorHandler,
warningHandler:dwr.engine._warningHandler,
textHtmlHandler:dwr.engine._textHtmlHandler
};
if(batch.xml==null)
{
batch.xml="aaa";
}
if (dwr.engine._preHook) batch.preHooks.push(dwr.engine._preHook);
if (dwr.engine._postHook) batch.postHooks.push(dwr.engine._postHook);
var propname, data;
if (dwr.engine._headers) {
for (propname in dwr.engine._headers) {
data = dwr.engine._headers[propname];
if (typeof data != "function") batch.headers[propname] = data;
}
}
if (dwr.engine._parameters) {
for (propname in dwr.engine._parameters) {
data = dwr.engine._parameters[propname];
if (typeof data != "function") batch.parameters[propname] = data;
}
}
return batch;
};
里面有很多信息,重要的就是Batch这个变量,里面定义了很多内容。如果对javascript不是很了解,就会感到很茫然,为什么这么说呢?请看下面定义的形式:
var batch = {
map:{
callCount:0,
page:window.location.pathname + window.location.search,
httpSessionId:dwr.engine._getJSessionId(),
scriptSessionId:dwr.engine._getScriptSessionId()
},
charsProcessed:0, paramCount:0,
parameters:{}, headers:{},
isPoll:false, handlers:{}, preHooks:[], postHooks:[],
rpcType:dwr.engine._rpcType,
httpMethod:dwr.engine._httpMethod,
async:dwr.engine._async,
timeout:dwr.engine._timeout,
errorHandler:dwr.engine._errorHandler,
warningHandler:dwr.engine._warningHandler,
textHtmlHandler:dwr.engine._textHtmlHandler
};
这里面定义的形式类似于一个键一个值(举例textHtmlHandler键:dwr.engine._textHtmlHandler值),这也是javascript中创建一个对象的一种方式,我们也可以通过这种方式来创建,我们可以把这种方式想象成一个键对应一个值,这就是一个对象当中的属性和属性对应的值。也可以想象成dom编程的方式,也就是文档对象模型,为什么可以想象成文档对象模型呢,这就是元素和元素中的内容。怎么想象都可以,但是这种方式我们要注意学习学习。说说里面的内容,创建batch(批处理),批处理可能很多次都要调用到,所以需要一个变量callCount:0指定批处理中需要调用多少次,初始化为0;page是指定请求的页面,window.location.pathname只的是请求的路径,window.location.search指的是路径问号(?)后面的信息; httpSessionId就是JSESSIONID;scriptSessionId 脚本SessionId
/** The function that we use to fetch/calculate a session id */
dwr.engine._getScriptSessionId = function() {
if (dwr.engine._scriptSessionId == null) {
dwr.engine._scriptSessionId = dwr.engine._origScriptSessionId + Math.floor(Math.random() * 1000);
}
return dwr.engine._scriptSessionId;
};
scriptSessionId是通过原始的脚本SessionId加上一个随机数得到的。parameters:{}参数; headers:{},http请求的头信息;handlers:{}处理器对象;preHooks:[],我把它叫做前勾,作用是在调用服务器的组件的时候,类似一个代理,如果preHooks存在,我先调用它的内容,然后再调用web组件的内容,最后调用postHooks。对于Demo.sayHello = function(p0,callback){dwr.engine._execute(Demo._path,'Demo','sayHello',p0,callback);},它会先调用preHooks的内容,接着执行请求,然后调用postHooks的内容。rpcType:dwr.engine._rpcType指的是请求的类型。因为dwr框架并不是仅仅针对xml、httprequest,它还会针对其他的请求类型:iframe、scriptshell脚本。httpMethod:dwr.engine._httpMethod指的是请求方法:post或者get,在dwr框架中,只允许这两种方式,其它的方式是不允许的。
async:dwr.engine._async,指的是异步还是同步,在ajax中,open的最后一个参数为true就是异步,false就是同步。如果为false来讲,用ajax就没有什么意义,所以一般来讲就是true.timeout:dwr.engine._timeout,指的是请求的超时时间。
接下来看execute方法是怎么将vararg_params分割长参数和回调函数的?
由于vararg_params里面包含了方法的参数以及回调函数,由于execute里面要调用回调函数,因此必须进行方法的参数和回调函数的分割。把参数放到参数的数组中,回调函数放到回调函数的变量中。
var args = [];用来放置方法的参数,去除回调函数以外的用来调用的方法的参数。
看看下面的代码:
for (var i = 0; i < arguments.length - 3; i++) {
args[i] = arguments[i + 3];
}
// All the paths MUST be to the same servlet
if (batch.path == null) {
batch.path = path;
}
else {
if (batch.path != path) {
dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
return;
}
}
在javascript中,创建一个函数,它有一个隐式的参量arguments,这个arguments相当于我这个函数调用时传进去的几个参数,而且这几个参数都是放置在arguments里面的,它并不是隐式的显示出来,因为弱类型的语言,参数列表个数不同可以调用同一个函数。arguments负责把所有的参数存放在里面。