第5章 腳本運行期庫對象
前面章節已經介紹了ASP如何使用在服務器上定義的對象的實例,充分利用所提供的方法和屬性擴展ASP的性能。有一系列的對象可供使用,包括腳本對象和標准IIS/ASP安裝的組件,以及自己創建的或者從其他供應商處購買的對象。也可以在互聯網上各種網站免費下載對象,並在自己的頁面上使用。
這一章將討論由ASP腳本環境提供的一般稱為“腳本運行期庫”(Scripting Runtime Library)的對象。這些對象通過正在使用的腳本引擎提供給代碼,與ASP腳本程序一起完成多種實用任務。
還有一種組件是“活動服務器組件”(Active Server Component),通過單獨的ActiveX DLL文件或者其他文件來實現。後面章節將討論相關內容。
當然,需要研究如何在頁面上使用這些對象。在前一章中,我們已經了解了服務器如何提供一個方法來實例化對象,本章將深入討論這個內容。
本章將介紹以下內容:
· 腳本引擎以腳本對象方式提供了什麼。
· 如何創建腳本對象及其他組件實例。
· 腳本對象的成員和屬性概要。
· 如何在代碼中使用腳本對象。
下面開始研究腳本對象的定義。
5.1 腳本對象的定義
前面章節研究了ASP對象模型。
對象模型是用來理解系統的各個部分相互關系的一種基本手段。
ASP對象模型提供了一種結構,用來作為一個整體操縱HTTP請求、響應及ASP環境中的不同元素。例如,我們已經看到,如何通過查看ASP請求對象的cookIE集合,得到來自浏覽器的任何cookIE值。
我們使用的腳本語言也有對象模型。然而,腳本語言提供的這一對象模型,不同於由ASP DLL直接提供的對象模型,腳本對象是由Microsoft腳本運行期庫(scrrun.dll)提供的,安裝缺省的Active Scripting腳本引擎時,也安裝了Microsoft腳本運行期庫。
5.1.1 不同類型的對象和組件
不要對“對象”和“組件”這兩個名詞感到困惑,在一定范圍內它們都可以作為ASP的一部分,同樣可以通過COM對其進行訪問。從概念上可以將它們分為四類:
· ASP內置對象,如ObjectContext、Request、Response、application、session、Server和ASPError。本書的第2章到第4章已經研究了這些內容。
· 腳本對象。通過腳本運行期庫使用,如Dictionary、FileSystem和TextStream。這是本章要討論的對象。
· 可安裝的組件。由Microsoft在IIS 5.0和ASP 3.0標准安裝時提供。這將在下一章討論。
· 其他組件。從其他獨立廠商購買的、在網站上發現的或者自己創建的組件。還有一些其他的由Windows服務或產品提供的組件,如Windows Scripting Host。在本書的附錄中提供了相應的列表,本書專門有一部分章節講述如何構建自己的組件。
5.1.2 VBScript和JScript腳本對象
作為腳本運行期庫的一部分,Microsoft提供三個主要的對象:
· Dictionary對象提供一個極為有用的存儲對象,它用來存儲值,通過對象的名字而不是其索引進行訪問和引用。例如,對於存儲從ASP Request對象中檢索到的名稱/值對,這是非常合適的。
· FileSystemObject對象提供了對服務器底層文件系統的訪問(在客戶端上使用IE 5.0,與名為“Hypertext Application(HTA)”的特殊類型的頁面協同使用)。可用FileSystemObject對象遍歷計算機的本地及網絡的驅動器、文件夾和文件。
· TextStream對象提供對存儲在磁盤上文件的訪問,用於同FileSystemObject對象協同使用。TextStream對象能夠讀出或寫入文本(順序的)文件,並僅能通過FileSystemObject對象進行實例化,所以人們常常認為TextStream對象是FileSystemObject對象的子對象。
FileSystemObject對象是其他一系列用來與文件系統交互的對象和集合的“父代”。該對象提供了對象的三個集合:Drives、Folders和Files集合,每個集合分別是相應的Drive、Folder和File對象的集合。它們用來進行磁盤上的驅動器、文件夾(目錄)和文件的遍歷和定位。對象間的關系如圖5-1所示:
[img]http://dave_lu.myetang.com/asppic/ASP79.jpg[/img]
圖5-1 腳本運行期庫中對象間的關系
下面,將依次介紹這些對象和集合,以及如何使用它們。然而,首先要理解對象實例與組件的創建或實例化方式之間的差異。這是下一節的主要內容。
5.2 創建對象生組件實例
創建腳本運行期庫對象的實例與創建任何其他對象和組件的實例化方式完全相同。可使用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+提供代理特性的對象是沒有益處的。