创建|对象
创建脚本运行期库对象的实例与创建任何其他对象和组件的实例化方式完全相同。可使用ASP Server对象提供的CreateObject方法(确保对象创建在当前页面的环境内),或者使用一个<OBJECT>元素。我们将研究这两种方法,究竟采用那种方法依赖于页面的需要。
5.2.1 使用Server.CreateObject方法
正如在研究Server对象的时候看到的,组件或其他对象实例可根据它们的ProgID来创建:
<%
Dim objThis
Set objThis = Server.CreateObject(“ADODB.Connection”)
%>
ProgID字符串“正式的”格式是“供应商.组件.版本”,供应商的名字和版本是可选的。通常ProgID只包含前两部分(如上例)。少数供应商在ProgID中设置版本编号,这将避免向后兼容的新版本使用同样的ProgID,这要求改变ASP页面才能使用新版本。
5.2.2 使用<OBJECT>元素
可以使用标准的HTML<OBJECT>元素通过增加RUNAT参数并指定其值为“SERVER”来在服务器上创建一个组件实例。另外,通常是提供对象的ProgID字符串而不是数字的ClassID:
<OBJECT ID=”objThis” RUNAT=”SERVER” PROGID=”This.Object”>
<PARAM NAME=”param1” VALUE=”value1”>
<PARAM NAME=”param2” VALUE=”value2”>
</OBJECT>
如果上面脚本的对象有相应的属性可在脚本中使用,在<OBJECT>元素内可通过<PARAM>元素进行设置,就像通常在HTML页面中所做的一样。在ASP中使用<OBJECT>元素时不要求CODEBASE属性,当其不可用时,服务器不会试图下载以及安装对象或组件。
1. 指定一个ClassID
另外,可以指定想要创建的对象或组件的ClassID。在不知道目标机安装了什么其他组件的情况下,这是非常有用的。例如在客户端上的浏览器的页面上实例化组件时。
在理论上,组件的ProgID(文本“供应商.组件”)不应该相互冲突,应该是唯一的。然而,这不是无懈可击的。有可能美国北方的一个供应商与希腊小岛上的一个供应商同名。但是,使用ClassID识别访问时,因为ClassID是唯一的,同名情况就不会发生。
如果决定使用对象或组件的ClassID,应将其放入CLASSID属性中,而不是PROGID属性。如:
<OBJECT ID=”objThis” RUNAT=”SERVER”
CLASSID=”clsid:892D6DA7-E0F9-11D2-B2E9-00105A42AF30”>
<PARAM NAME=”param1” VALUE=”value1”>
<PARAM NAME=”param2” VALUE=”value2”>
</OBJECT>
但在自己的服务器上实例化对象时,应该知道对象和组件的安装方式。这样在ASP代码中创建对象实例时,可以安全地使用ProgID。这就是ClassID很少在ASP页面内使用的原因。然而,因为ProgID用于查找ClassID,如果愿意也可以用组件或对象的ClassID代替ProgID。
2. 设置对象实例的作用域
缺省情况下,所有ASP页面中创建的对象与组件实例(无论用Server.CreateObject方法或<OBJECT>元素)都有页面内的作用域(page scope)。这意味着,对象与组件只有该页在ASP上运行时才存在,当页面完成并且把结果发送到客户端以后就自动地取消了。
然而,如果在global.asa文件(它存在于站点或虚拟应用程序的根目录)中放置<OBJECT>声明,可以将对象或组件的作用域指定为应用程序或会话作用域。
(1) 在应用程序层作用域创建对象
通过设置SCOPE属性为“APPLICATION”,创建应用程序层作用域对象:
<OBJECT ID=”objThis” RUNAT=”SERVER” PROGID=”This.Object”
SCOPE=”APPLICATION”>
</OBJECT>
应用程序开始时创建了对象实例,即一旦用户从虚拟应用程序的目录请求一个页面,就创建对象实例。对于缺省Web站点,这可以是站点上的任一目录。直到应用程序结束(最后的用户会话结束)前,对象实例一直存在,并且可以被虚拟应用程序或站点目录内任一页面内的任意用户引用和使用。
(2) 在会话层作用域创建对象
如果想创建由单个用户使用的对象实例,其作用域为他访问的所有页面,可创建会话层作用域对象。这通过将SCOPE属性设置为“SESSION”来实现:
<OBJECT ID=”objThis” RUNAT=”SERVER” PROGID=”This.Object”
SCOPE=SESSION”>
</OBJECT>
对象一旦被引用就被创建,引用是由用户从虚拟应用程序或站点载入的页面内的程序代码完成的(在global.asa文件中有<OBJECT>声明)。当用户会话生命周期结束并被取消时,它引用的对象实例也就取消了。
(3) 关于作用域和状态
使对象实例的作用域为全局的或者为用户会话全局环境看起来是一个好主意,但在实际使用时有些问题需要考虑,其中之一是在用户的许多请求之间能够有效地保护对象的状态。换句话说,可以设定对象的一些属性,它们对使用的所有页面是共用的。因为不必每次都创建新的实例并设置其属性,所以这看起来是个较好的办法。
事实上,微软建议一般情况下不要这样做,这一思想是传统程序设计思想的残余。在Web上,要面对的最大问题是服务器以及Web应用程序及所提供的动态网页如何应付数以百万计的网站访问者。将组件实例驻留在内存中等待一个特定用户的页面请求,对可能有几百个用户同时浏览的网站来说,这样做不能有效地使用资源。
Windows 2000提供新的COM+运行期特性,它能够处理组件的创建、缓存和使用,采用一种吞吐量最大化但所占服务器资源最小化的方式。对象实例存储在哪里和存储多久的问题,最好由操作系统自己完成,而不是由程序员决定。
也就是说,在页面内需要的地方创建对象实例,当页面终止时让其消失。COM+整理这些碎片,自动处理后台的一些复杂工作。如果要了解有关这方面的内容,第14章比较详细地研究了组件的创建。
当然,在某种情况下,我们可能要求一个对象具有应用程序层和会话层的作用域,尤其是在页面请求间保存状态时。在后面讨论Dictionary对象时,将有一个这方面的实例。
5.2.3 Server.CreateObject与<OBJECT>的区别
Server.CreateObject方法立即创建一个对象实例。在大多数情况下这也是我们所希望的。而<OBJECT>元素只有首次引用一个对象时才创建指定的对象实例。因此如果在代码中停止使用该对象,则不创建该对象实例。
如果代码只在某种情况下使用这个对象(可能依赖于请求参数的值),这也许是有用的。因为如果不需要这个对象,则可以节省服务器的资源。
然而,如果肯定需要创建某一对象,可使用Server.CreateObject方法完成。用<OBJECT>元素创建对象有助于防止在代码中取消对对象的调用时,忘记取消程序中的Server.CreateObject行,当然这是一个粗心的程序设计。
最后需要记住的是,如果对象是使用Server.CreateObject方法创建的,就可以从会话或应用程序中去掉对象,但使用<OBJECT>元素创建的,则不行。
5.2.4 组件线程模型
在页面内使用对象或组件时,应该考虑的另一个问题是该对象涉及到的响应多个请求的行为方式。事实上在ASP里,这是所需要理解的最复杂的题目之一。一个组件的线程模型,结合其作用域,影响该组件和应用程序的性能和效率,也影响将它实例化的ASP页面。
线程就是由处理器执行的系统对象,用于完成由组件代码定义的任务。每一个线程都可以被认为是单个二进制指令集。在像Windows这样的多线程环境中,多个线程可同时运行。
实际上有五个线程模型(包括在Windows 2000里引入的Neutral-threading模型):
· Single-threaded(单线程):某一时刻只能有一个进程使用某组件。
· Apartment-threaded(单元线程):若干进程都可以使用某组件,但只有一个在指定的线程上。
· Neutral-threaded(中立线程):若干进程都能使用某组件,并且可以使用指定的一组线程中的任何一个。
· Multiple-threaded或Free-threaded(多线程或自由线程):若干进程都能使用某组件,并且这些进程可以运行在不同的线程上。
· Both-threaded(双线程):对象既可以是单元线程的又可以作为自由线程的。
在这里不解释线程模型的技术细节,本书后面有相应的内容。
单元线程的组件(例如使用Visual Basic创建的或作为XML脚本的组件)可在页面层作用域内很好地运行,在会话层作用域内也是可以接受的。事实上,在页面层,由于较低的数据处理开销,也能很好地运行双线程的组件。
Winodws 2000中的中立线程的模型甚至提供了更好的性能,尽管到目前为止只有很少的这样的组件和与之相适应的开发工具。
如果需要会话层组件,使用可用的双线程的组件。并且如果需要应用程序层作用域,可一直使用双线程的组件。
然而,微软建议避免使用会话层作用域的组件,甚至不使用应用程序层作用域的组件,除非这些组件是绝对需要的。使组件的活动时间超过作用域为页面级的组件所要求的时间,对于由COM+提供代理特性的对象是没有益处的。5.2.5 引用对象类型库
在早先的ASP版本中,在脚本中使用对象或组件时,组件内定义的公共常数(如果有的话)在ASP里将不再有效。这意味着我们需要自己声明它们(或等价物)并指定相应的值。
例如,当在早期版本的ASP中使用ActiveX数据库对象(ADO)组件时(将在第8章进行详细的研究),不得不用记录集的Open方法加入预定义常数声明。例如:
Const adOpenKeyset = &H0001
Const adLockPessimistic = &H0003
Const adCmdTable = &H0002
…
rs.Open “Contact”,”DSN=GlobalExampleData;UID=examples;Password=;”, _
adOpenKeyset, adLockPessimistic, adCmdTable
…
另一种方法是使用#include指令在页面插入一个名为adovbs.inc的文件。该文件由IIS/ASP提供,包含ADO所需的所有预定义常数。更新代码时,必须确认使用的是最新版本,并检查它对于所有的页面请求都可用。
对于IIS 5.0,有一个更好的方法,通过在HTML注释元素内使用METADATA指令,可以给组件或对象的类型库增加引用(IIS 4.0不支持这一功能)。
<!-- METADATA TYPE=”TypeLib”
FILE=”path_and_name_of_file”
UUID=”type_library_uuid”
VERSSION=”major_version_number.minor_version_number”
LCID=”locale_id”
-->
其中:
· path_and_name_of_file是某一类型库文件(.tlb)或ActiveX DLL的绝对物理路径,必须提供这一参数或者是type_library_uuid参数。
· type_library_uuid是该类型库的唯唯一标识符,必须提供这一参数或者是path_and_name_of_file参数。
· major_version_number.minor_version_number(可选)定义了所需组件的版本。如果没有该版本则使用最近的版本。
· locale_id(可选)是区域标志符。如果在该区域没有发现类型库,计算机将使用缺省的(安装时定义的)区域。
因此,使用这一技术,通过使用下面的代码,能使内置的ADO预定义常数在ASP页面可用:
<!-- METADATA TYPE=”TypeLib”
FILE=”C:Program FilesCommon FilesSystemadomsado15.dll”
-->
文件名msado15.dll还可用于更高版本(2.50以后)的ADO组件。
如果ASP不能装载类型库,就返回一个错误并停止该页的执行。可能的错误提示如表5-1所示:
表5-1 错误提示代码及说明
错 误 说 明
ASP 0222 无效的类型库说明
ASP 0223 未找到类型库
ASP 0224 类型库不能加载
ASP 0225 类型库不能打包(即ASP不能从指定的类型库中创建类型库包装对象)
5.2.6 在客户端上创建对象实例
在ASP中讨论在服务器上实例化对象和组件的技术时,值得强调的是在浏览器中运行客户端页面而完成同样工作的方式。如果你使用ASP创建包含客户端脚本程序的页面,或者使用<OBJECT>元素创建客户端组件实例,将会发现这是非常有用的。在大多数情况下,脚本运行期对象可在客户端上实例化和使用,效果与服务器上的ASP相同。
1. VBScript CreateObject方法
在客户端使用CreateObject时,在浏览器的环境内创建组件或对象实例,它们与浏览器运行在相同的内存空间里(即进程内),除非实现的对象是带有.exe扩展文件名的可执行文件。
通常指定对象的ClassID,而不是使用ProgID字符串,这样就不可能与其他安装在客户端的对象发生冲突。
<SCRIPT LANGUAGE=”VBScript”>
Dim objThis
Set objThis = CreateObject(“clsid:892D6DA7-E0F9-00105A42AF30”)
…
</SCRIPT>
当然也可以使用ProgID,并且使用通用的对象或组件(特别是标准安装提供的对象或组件),那么得到错误的组件的风险是很小的:
<SCRIPT LANGUAGE=”VBScript”>
Dim objThis
Set objThis = CreateObject(“Scripting.Dictionary”)
…
</SCRIPT>
2. Jscript ActiveXObject方法
为了在客户端上实例化Jscript的对象和组件,必须使用ActiveXObject方法和new操作符:
<SCRIPT LANGUAGE=”JScript”>
var objMyData = new ActiveXObject(‘clsid: 892D6DA7-E0F9-00105A42AF30’);
</SCRIPT>
或:
<SCRIPT LANGUAGE=”JScript”>
var objMyData = new ActiveXObject(‘this.object’);
</SCRIPT>
3. <OBJECT>元素技术
也可使用<OBJECT>元素创建客户端对象或组件的实例。应省略RUNAT属性或者将其设定为“CLIENT”。然而,这个属性在客户端上是被忽略的,因此设置这个属性的唯一目的就是,在ASP页面使用<OBJECT>元素实例化服务器端的组件实例时防止混淆。
<OBJECT ID=”objThis” RUNAT=”CLIENT”
CLASSID=”clsid: 892D6DA7-E0F9-00105A42AF30”
CODEBASE=”http://yourserver.com/components/mycomponent.cab”>
<PARAM NAME=”param1” VALUE=”value1”>
<PARAM NAME=”param2” VALUE=”value2”>
</OBJECT>
注意,这里出现的CODEBASE属性,表示允许下载并安装来自URL的组件(如果该组件没有安装)。IE 3.0以上的版本有此功能。
对于使用<OBJECT>元素的方法、可使用的属性、在客户端使用的值,可查看网站http://msdn.Microsoft.com/workshop/author/dhtml/reference/objects/OBJECT.asp,或者Windows 2000 Platform SDK文档中的<OBJECT> tags,或者看看《IE5 Dynamic HTML Programmer’ Reference》一书,ISBN 1-861001-74-6,Wrox出版社。