posts - 189,comments - 115,trackbacks - 0

          ?

          Java怎樣調(diào)用外部應(yīng)用程序
          [轉(zhuǎn)貼]???

          import?java.io.*;
          class?Runtime1
          {
          ????public?Runtime1()?
          ????{
          ????????try{
          ????????Runtime.getRuntime().exec("C:\\Program?Files\\Microsoft?Visual?Studio\\Common\\MSDev98\\Bin\\MSDEV.EXE");????
          ????????}
          ????????catch(Exception?e)
          ????????{
          ????????}
          ????}
          ????public?static?void?main(String?[]args)
          ????{
          ????????new?Runtime1();
          ????}
          }
          //把代碼第7行exec后面的括號里里面換成你應(yīng)用程序的路徑即可,注意路徑加雙斜桿.


          用Java?Socket開發(fā)小型服務(wù)器,支持上千個并發(fā)
          [轉(zhuǎn)]

          Java?Socket
          套接字(socket)為兩臺計(jì)算機(jī)之間的通信提供了一種機(jī)制,在James?Gosling注意到Java?語言之前,套接字就早已赫赫有名。該語言只是讓您不必了解底層操作系統(tǒng)的細(xì)節(jié)就能有效地使用套接字。
          1?客戶機(jī)/服務(wù)器模型
          在飯店里,菜單上各種具有異國情調(diào)的食品映入你的眼簾,于是你要了一份pizza。幾分鐘后,你用力咀嚼澆著融化的乳酪和其他你喜歡的配料的熱pizza。你不知道,也不想知道:侍者從那里弄來了pizza,在制作過程中加進(jìn)了什么,以及配料是如何獲得的。
          上例中包含的實(shí)體有:美味的pizza、接受你定餐的侍者、制作pizza的廚房,當(dāng)然還有你。你是定pizza的顧客或客戶。制作pizza的過程對于你而言是被封裝的。你的請求在廚房中被處理,pizza制作完成后,由侍者端給你。
          你所看到的就是一個客戶機(jī)/服務(wù)器模型。客戶機(jī)向服務(wù)器發(fā)送一個請求或命令。服務(wù)器處理客戶機(jī)的請求。客戶機(jī)和服務(wù)器之間的通訊是客戶機(jī)/服務(wù)器模型中的一個重要組成部分,通常通過網(wǎng)絡(luò)進(jìn)行。
          客戶機(jī)/服務(wù)器模型是一個應(yīng)用程序開發(fā)框架,該框架是為了將數(shù)據(jù)的表示與其內(nèi)部的處理和存儲分離開來而設(shè)計(jì)的。客戶機(jī)請求服務(wù),服務(wù)器為這些請求服務(wù)。請求通過網(wǎng)絡(luò)從客戶機(jī)傳遞到服務(wù)器。服務(wù)器所進(jìn)行的處理對客戶機(jī)而言是隱藏的。一個服務(wù)器可以為多臺客戶機(jī)服務(wù)。
          ?多臺客戶機(jī)訪問服務(wù)器
          服務(wù)器和客戶機(jī)不一定是硬件組件。它們可以是工作啊同一機(jī)器或不同機(jī)器上的程序。、
          考慮一個航空定票系統(tǒng)中的數(shù)據(jù)輸入程序:數(shù)據(jù)----乘客名、航班號、飛行日期、目的地等可以被輸入到前端----客戶機(jī)的應(yīng)用程序中。一旦數(shù)據(jù)輸入之后,客戶機(jī)將數(shù)據(jù)發(fā)送到后端----服務(wù)器端。服務(wù)器處理數(shù)據(jù)并在數(shù)據(jù)庫中保存數(shù)據(jù)??蛻魴C(jī)/服務(wù)器模型的重要性在于所有的數(shù)據(jù)都存放在同一地點(diǎn)??蛻魴C(jī)從不同的地方訪問同一數(shù)據(jù)源,服務(wù)器對所有的輸入數(shù)據(jù)應(yīng)用同樣的檢驗(yàn)規(guī)則。
          萬維網(wǎng)為‘為什么要將數(shù)據(jù)的表示與其存儲、處理分離開來’提供了一個很好的例子。在Web上,你無需控制最終用戶用來訪問你數(shù)據(jù)的平臺和軟件。你可以考慮編寫出適用與每一種潛在的目標(biāo)平臺的應(yīng)用程序。
          ‘客戶機(jī)/服務(wù)器應(yīng)用程序的服務(wù)器部分’管理通過多個客戶機(jī)訪問服務(wù)器的、多個用戶共享的資源。表明‘客戶機(jī)/服務(wù)器程序的服務(wù)器部分’強(qiáng)大功能的最好例子應(yīng)該是Web服務(wù)器,它通過Internet將HTML頁傳遞給不同的Web用戶。
          Java編程語言中最基本的特點(diǎn)是在Java中創(chuàng)建的程序的代碼的可移植性。因?yàn)榫哂衅渌Z言所不具備的代碼可移植性,Java允許用戶只要編寫一次應(yīng)用程序,就可以在任何客戶機(jī)系統(tǒng)上發(fā)布它,并可以讓客戶機(jī)系統(tǒng)解釋該程序。這意味著:你只要寫一次代碼,就能使其在任何平臺上運(yùn)行。

          2?協(xié)議
          當(dāng)你同朋友交談時(shí),你們遵循一些暗含的規(guī)則(或協(xié)議)。例如:你們倆不能同時(shí)開始說話,或連續(xù)不間斷地說話。如果你們這樣作的話,誰也不能理解對方所說的東西。當(dāng)你說話時(shí),你的朋友傾聽,反之亦然。你們以雙方都能理解的語言和速度進(jìn)行對話。
          當(dāng)計(jì)算機(jī)之間進(jìn)行通訊的時(shí)候,也需要遵循一定的規(guī)則。數(shù)據(jù)以包的形式從一臺機(jī)器發(fā)送到另一臺。這些規(guī)則管理數(shù)據(jù)打包、數(shù)據(jù)傳輸速度和重新?數(shù)據(jù)將其恢復(fù)成原始形式。這些規(guī)則被稱為網(wǎng)絡(luò)協(xié)議。網(wǎng)絡(luò)協(xié)議是通過網(wǎng)絡(luò)進(jìn)行通訊的系統(tǒng)所遵循的一系列規(guī)則和慣例。連網(wǎng)軟件通常實(shí)現(xiàn)有高低層次之分的多層協(xié)議。網(wǎng)絡(luò)協(xié)議的例子有:TCP/IP、UDP、Apple?Talk和NetBEUI。
          Java提供了一個豐富的、支持網(wǎng)絡(luò)的類庫,這些類使得應(yīng)用程序能方便地訪問網(wǎng)絡(luò)資源。Java提供了兩種通訊工具。它們是:使用用戶報(bào)文協(xié)議(UDP)的報(bào)文和使用傳輸控制協(xié)議/因特網(wǎng)協(xié)議(TCP/IP)的Sockets(套接字)。
          數(shù)據(jù)報(bào)包是一個字節(jié)數(shù)組從一個程序(發(fā)送程序)傳送到另一個(接受程序)。由于數(shù)據(jù)報(bào)遵守UDP,不保證發(fā)出的數(shù)據(jù)包必須到達(dá)目的地。數(shù)據(jù)報(bào)并不是可信賴的。因此,僅當(dāng)傳送少量數(shù)據(jù)時(shí)才使用,而且發(fā)送者和接受者之間的距離間隔不大,假如是網(wǎng)絡(luò)交通高峰,或接受程序正處理來自其他程序的多個請求,就有機(jī)會出現(xiàn)數(shù)據(jù)報(bào)包的丟失。
          Sockets套接字用TCP來進(jìn)行通訊。套接字模型同其他模型相比,優(yōu)越性在于其不受客戶請求來自何處的影響。只要客戶機(jī)遵循TCP/IP協(xié)議,服務(wù)器就會對它的請求提供服務(wù)。這意味著客戶機(jī)可以是任何類型的計(jì)算機(jī)??蛻魴C(jī)不再局限為UNIX、Windows、DOS或Macintosh平臺,因此,網(wǎng)上所有遵循TCP/IP協(xié)議的計(jì)算機(jī)可以通過套接字互相通訊。

          3?Sockets套接字
          3.1?Sockets概況
          在客戶機(jī)/服務(wù)器應(yīng)用程序中,服務(wù)器提供象處理數(shù)據(jù)庫查詢或修改數(shù)據(jù)庫中的數(shù)據(jù)之類的服務(wù)。發(fā)生在客戶機(jī)和服務(wù)器之間的通訊必須是可靠的,同時(shí)數(shù)據(jù)在客戶機(jī)上的次序應(yīng)該和服務(wù)器發(fā)送出來的次序相同。
          什么是套接字??
          既然我們已經(jīng)知道套接字扮演的角色,那么剩下的問題是:什么是套接字?Bruce?Eckel?在他的《Java?編程思想》一書中這樣描述套接字:套接字是一種軟件抽象,用于表達(dá)兩臺機(jī)器之間的連接“終端”。對于一個給定的連接,每臺機(jī)器上都有一個套接字,您也可以想象它們之間有一條虛擬的“電纜”,“電纜”的每一端都插入到套接字中。當(dāng)然,機(jī)器之間的物理硬件和電纜連接都是完全未知的。抽象的全部目的是使我們無須知道不必知道的細(xì)節(jié)。?
          簡言之,一臺機(jī)器上的套接字與另一臺機(jī)器上的套接字交談就創(chuàng)建一條通信通道。程序員可以用該通道來在兩臺機(jī)器之間發(fā)送數(shù)據(jù)。當(dāng)您發(fā)送數(shù)據(jù)時(shí),TCP/IP?協(xié)議棧的每一層都會添加適當(dāng)?shù)膱?bào)頭信息來包裝數(shù)據(jù)。這些報(bào)頭幫助協(xié)議棧把您的數(shù)據(jù)送到目的地。好消息是?Java?語言通過"流"為您的代碼提供數(shù)據(jù),從而隱藏了所有這些細(xì)節(jié),這也是為什么它們有時(shí)候被叫做流套接字(streaming?socket)的原因。
          把套接字想成兩端電話上的聽筒,我和您通過專用通道在我們的電話聽筒上講話和聆聽。直到我們決定掛斷電話,對話才會結(jié)束(除非我們在使用蜂窩電話)。而且我們各自的電話線路都占線,直到我們掛斷電話。
          如果想在沒有更高級機(jī)制如?ORB(以及?CORBA、RMI、IIOP?等等)開銷的情況下進(jìn)行兩臺計(jì)算機(jī)之間的通信,那么套接字就適合您。套接字的低級細(xì)節(jié)相當(dāng)棘手。幸運(yùn)的是,Java?平臺給了您一些雖然簡單但卻強(qiáng)大的更高級抽象,使您可以容易地創(chuàng)建和使用套接字。
          傳輸控制協(xié)議(TCP)提供了一條可靠的、點(diǎn)對點(diǎn)的通訊通道,客戶機(jī)/服務(wù)器應(yīng)用程序可以用該通道互相通訊。要通過TCP進(jìn)行通訊,客戶機(jī)和服務(wù)器程序建立連接并綁定套接字。套接字用于處理通過網(wǎng)絡(luò)連接的應(yīng)用程序之間的通訊??蛻魴C(jī)和服務(wù)器之間更深入的通訊通過套接字完成。
          Java被設(shè)計(jì)成一種連網(wǎng)語言。它通過將連接功能封裝到套接字類里而使得網(wǎng)絡(luò)編程更加容易。套接字類即Socket類(它創(chuàng)建一個客戶套接字)和ServerSocket類(它創(chuàng)建一個服務(wù)器套接字)。套接字類大致介紹如下:
          l????Socket是基類,它支持TCP協(xié)議。TCP是一個可靠的流網(wǎng)絡(luò)連接協(xié)議。Socket類提供了流輸入/輸出的方法,使得從套接字中讀出數(shù)據(jù)和往套接字中寫數(shù)據(jù)都很容易。該類對于編寫因特網(wǎng)上的通訊程序而言是必不可少的。
          l????ServerSocket是一個因特網(wǎng)服務(wù)程序用來監(jiān)聽客戶請求的類。ServerSocket實(shí)際上并不執(zhí)行服務(wù);而是創(chuàng)建了一個Socket對象來代表客戶機(jī)。通訊由創(chuàng)建的對象來完成。
          3.2?IP地址和端口
          因特網(wǎng)服務(wù)器可以被認(rèn)為是一組套接字類,它們提供了一般稱為服務(wù)的附加功能。服務(wù)的例子有:電子郵件、遠(yuǎn)程登錄的Telnet、和通過網(wǎng)絡(luò)傳輸文件的文件傳輸協(xié)議(FTP)。每種服務(wù)都與一個端口相聯(lián)系。端口是一個數(shù)值地址,通過它來處理服務(wù)請求(就象請求Web頁一樣)。
          TCP協(xié)議需要兩個數(shù)據(jù)項(xiàng):IP地址和端口號。因此,當(dāng)鍵入http://www.jinnuo.com/時(shí),你是如何進(jìn)入金諾的主頁呢?
          因特網(wǎng)協(xié)議(IP)提供每一項(xiàng)網(wǎng)絡(luò)設(shè)備。這些設(shè)備都帶有一個稱為IP地址的邏輯地址。由因特網(wǎng)協(xié)議提供的IP地址具有特定的形式。每個IP地址都是32位的數(shù)值,表示4個范圍在0到255之間的8位數(shù)值金諾已經(jīng)注冊了它的名字,分配給http://www.jinnuo.com/的IP地址為192.168.0.110。
          注意:域名服務(wù)或DNS服務(wù)是將http://www.jinnuo.com/翻譯成192.168.0.110的服務(wù)。這使你可以鍵入http://www.jinnuo.com/而不必記住IP地址。想象一下,怎么可能記住所有需要訪問的站點(diǎn)的IP地址!有趣的是一個網(wǎng)絡(luò)名可以映射到許多IP地址。對于經(jīng)常訪問的站點(diǎn)可能需要這一功能,因?yàn)檫@些站點(diǎn)容納大量的信息,并需要多個IP地址來提供業(yè)務(wù)服務(wù)。例如:192.168.0.110的實(shí)際的內(nèi)部名稱為http://www.jinnuo.com/。DNS可以將分配給jinnuo?Ltd.的一系列IP地址翻譯成http://www.jinnuo.com/。
          如果沒有指明端口號,則使用服務(wù)文件中服務(wù)器的端口。每種協(xié)議有一個缺省的端口號,在端口號未指明時(shí)使用該缺省端口號。
          端口號????應(yīng)用
          21????FTP.傳輸文件
          23????Telnet.提供遠(yuǎn)程登錄
          25????SMTP.傳遞郵件信息
          67????BOOTP.在啟動時(shí)提供配置情況
          80????HTTP.傳輸Web頁
          109????POP.使用戶能訪問遠(yuǎn)程系統(tǒng)中的郵箱
          讓我們再來看一下URL:http://www.jinnuo.com/
          URL的第一部分(http)意味著你正在使用超文本傳輸協(xié)議(HTTP),該協(xié)議處理Web文檔。如果沒有指明文件,大多數(shù)的Web服務(wù)器會取一個叫index.html文件。因此,IP地址和端口既可以通過明確指出URL各部分來決定,也可以由缺省值決定。
          4?創(chuàng)建Socket客戶
          我們將在本部分討論的示例將闡明在?Java?代碼中如何使用?Socket?和?ServerSocket。客戶機(jī)用?Socket?連接到服務(wù)器。服務(wù)器用?ServerSocket?在端口?1001?偵聽??蛻魴C(jī)請求服務(wù)器?C:?驅(qū)動器上的文件內(nèi)容。
          創(chuàng)建?RemoteFileClient?類
          import?java.io.*;
          import?java.net.*;
          public?class?RemoteFileClient?{
          ????protected?BufferedReader?socketReader;
          ????protected?PrintWriter?socketWriter;
          ????protected?String?hostIp;
          ????protected?int?hostPort;
          ????//構(gòu)造方法
          ????public?RemoteFileClient(String?hostIp,?int?hostPort)?{
          ????????this.hostIp?=?hostIp;
          ????????this.hostPort=hostPort;?
          ????}
          ????//向服務(wù)器請求文件的內(nèi)容
          ????public?String?getFile(String?fileNameToGet)?{
          ????????StringBuffer?fileLines?=?new?StringBuffer();
          ????????try?{
          ????????????socketWriter.println(fileNameToGet);????????????
          ????????????socketWriter.flush();
          ????????????String?line?=?null;
          ????????????while((line=socketReader.readLine())!=null)
          ????????????????fileLines.append(line+"\n");
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println("Error?reading?from?file:?"+fileNameToGet);
          ????????}
          ????????return?fileLines.toString();
          ????}
          ????//連接到遠(yuǎn)程服務(wù)器
          ????public?void?setUpConnection()?{
          ????????try?{
          ????????????Socket?client?=?new?Socket(hostIp,hostPort);
          ????????????socketReader?=?new?BufferedReader(new?InputStreamReader(client.getInputStream()));
          ????????????socketWriter?=?new?PrintWriter(client.getOutputStream());
          ????????}
          ????????catch(UnknownHostException?e)?{
          ????????????System.out.println("Error1?setting?up?socket?connection:?unknown?host?at?"+hostIp+":"+hostPort);
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println("Error2?setting?up?socket?connection:?"+e);
          ????????}
          ????}
          ????//斷開遠(yuǎn)程服務(wù)器
          ????public?void?tearDownConnection()?{
          ????????try?{
          ????????????socketWriter.close();?
          ????????????socketReader.close();
          ????????}catch(IOException?e)?{????????????
          ????????????System.out.println("Error?tearing?down?socket?connection:?"+e);
          ????????}
          ????}
          ????public?static?void?main(String?args[])?{
          ????????RemoteFileClient?remoteFileClient?=?new?RemoteFileClient("127.0.0.1",1001);
          ????????remoteFileClient.setUpConnection();
          ????????StringBuffer?fileContents?=?new?StringBuffer();
          ????????fileContents.append(remoteFileClient.getFile("RemoteFileServer.java"));????????
          ????????//remoteFileClient.tearDownConnection();
          ????????System.out.println(fileContents);
          ????}
          }
          首先我們導(dǎo)入?java.net?和?java.io。java.net?包為您提供您需要的套接字工具。java.io?包為您提供對流進(jìn)行讀寫的工具,這是您與?TCP?套接字通信的唯一途徑。
          我們給我們的類實(shí)例變量以支持對套接字流的讀寫和存儲我們將連接到的遠(yuǎn)程主機(jī)的詳細(xì)信息。
          我們類的構(gòu)造器有兩個參數(shù):遠(yuǎn)程主機(jī)的IP地址和端口號各一個,而且構(gòu)造器將它們賦給實(shí)例變量。
          我們的類有一個?main()?方法和三個其它方法。稍后我們將探究這些方法的細(xì)節(jié)。現(xiàn)在您只需知道?setUpConnection()?將連接到遠(yuǎn)程服務(wù)器,getFile()?將向遠(yuǎn)程服務(wù)器請求?fileNameToGet?的內(nèi)容以及?tearDownConnection()?將從遠(yuǎn)程服務(wù)器上斷開。
          實(shí)現(xiàn)?main()
          這里我們實(shí)現(xiàn)?main()?方法,它將創(chuàng)建?RemoteFileClient?并用它來獲取遠(yuǎn)程文件的內(nèi)容,然后打印結(jié)果。main()?方法用主機(jī)的?IP?地址和端口號實(shí)例化一個新?RemoteFileClient(客戶機(jī))。然后,我們告訴客戶機(jī)建立一個到主機(jī)的連接。接著,我們告訴客戶機(jī)獲取主機(jī)上一個指定文件的內(nèi)容。最后,我們告訴客戶機(jī)斷開它到主機(jī)的連接。我們把文件內(nèi)容打印到控制臺,只是為了證明一切都是按計(jì)劃進(jìn)行的。
          建立連接
          這里我們實(shí)現(xiàn)?setUpConnection()?方法,它將創(chuàng)建我們的?Socket?并讓我們訪問該套接字的流:
          ????public?void?setUpConnection()?{
          ????????try?{
          ????????????Socket?client?=?new?Socket(hostIp,hostPort);
          ????????????socketReader?=?new?BufferedReader(new?InputStreamReader(client.getInputStream()));
          ????????????socketWriter?=?new?PrintWriter(client.getOutputStream());
          ????????}
          ????????catch(UnknownHostException?e)?{
          ????????????System.out.println("Error1?setting?up?socket?connection:?unknown?host?at?"+hostIp+":"+hostPort);
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println("Error2?setting?up?socket?connection:?"+e);
          ????????}
          ????}
          setUpConnection()?方法用主機(jī)的?IP?地址和端口號創(chuàng)建一個?Socket:
          Socket?client?=?new?Socket(hostIp,?hostPort);
          我們把?Socket?的?InputStream?包裝進(jìn)?BufferedReader?以使我們能夠讀取流的行。然后,我們把?Socket?的?OutputStream?包裝進(jìn)?PrintWriter?以使我們能夠發(fā)送文件請求到服務(wù)器:
          socketReader?=?new?BufferedReader(new?InputStreamReader(client.getInputStream()));socketWriter?=?new?PrintWriter(client.getOutputStream());
          請記住我們的客戶機(jī)和服務(wù)器只是來回傳送字節(jié)。客戶機(jī)和服務(wù)器都必須知道另一方即將發(fā)送的是什么以使它們能夠作出適當(dāng)?shù)捻憫?yīng)。在這個案例中,服務(wù)器知道我們將發(fā)送一條有效的文件路徑。
          當(dāng)您實(shí)例化一個?Socket?時(shí),將拋出?UnknownHostException。這里我們不特別處理它,但我們打印一些信息到控制臺以告訴我們發(fā)生了什么錯誤。同樣地,當(dāng)我們試圖獲取?Socket?的?InputStream?或?OutputStream?時(shí),如果拋出了一個一般?IOException,我們也打印一些信息到控制臺。
          與主機(jī)交談
          這里我們實(shí)現(xiàn)?getFile()?方法,它將告訴服務(wù)器我們想要什么文件并在服務(wù)器傳回其內(nèi)容時(shí)接收該內(nèi)容。
          ????public?String?getFile(String?fileNameToGet)?{
          ????????StringBuffer?fileLines?=?new?StringBuffer();
          ????????try?{
          ????????????socketWriter.println(fileNameToGet);????????????
          ????????????socketWriter.flush();
          ????????????String?line?=?null;
          ????????????while((line=socketReader.readLine())!=null)
          ????????????????fileLines.append(line+"\n");
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println("Error?reading?from?file:?"+fileNameToGet);
          ????????}
          ????????return?fileLines.toString();
          ????}
          對getFile()方法的調(diào)用要求一個有效的文件路徑String。它首先創(chuàng)建名為fileLines的?StringBuffer,fileLines?用于存儲我們讀自服務(wù)器上的文件的每一行。
          StringBuffer?fileLines?=?new?StringBuffer();
          在?try{}catch{}?塊中,我們用?PrintWriter?把請求發(fā)送到主機(jī),PrintWriter?是我們在創(chuàng)建連接期間建立的。
          ????socketWriter.println(fileNameToGet);????socketWriter.flush();
          請注意這里我們是?flush()?該?PrintWriter,而不是關(guān)閉它。這迫使數(shù)據(jù)被發(fā)送到服務(wù)器而不關(guān)閉?Socket。
          一旦我們已經(jīng)寫到?Socket,我們就希望有一些響應(yīng)。我們不得不在?Socket?的?InputStream?上等待它,我們通過在?while?循環(huán)中調(diào)用?BufferedReader?上的?readLine()?來達(dá)到這個目的。我們把每一個返回行附加到?fileLines?StringBuffer(帶有一個換行符以保護(hù)行):
          ????String?line?=?null;????while((line=socketReader.readLine())!=null)????????fileLines.append(line+"\n");
          斷開連接
          這里我們實(shí)現(xiàn)?tearDownConnection()?方法,它將在我們使用完畢連接后負(fù)責(zé)“清除”。tearDownConnection()方法只是分別關(guān)閉我們在Socket的InputStream和OutputStream上創(chuàng)建的?BufferedReader和PrintWriter。這樣做會關(guān)閉我們從Socket獲取的底層流,所以我們必須捕捉可能的?IOException。
          總結(jié)一下客戶機(jī)
          我們的類研究完了。在我們繼續(xù)往前討論服務(wù)器端的情況之前,讓我們回顧一下創(chuàng)建和使用?Socket?的步驟:
          1.????用您想連接的機(jī)器的?IP?地址和端口實(shí)例化?Socket(如有問題則拋出?Exception)。
          2.????獲取?Socket?上的流以進(jìn)行讀寫。
          3.????把流包裝進(jìn)?BufferedReader/PrintWriter?的實(shí)例,如果這樣做能使事情更簡單的話。
          4.????對?Socket?進(jìn)行讀寫。
          5.????關(guān)閉打開的流。?
          5?創(chuàng)建服務(wù)器Socket
          創(chuàng)建?RemoteFileServer?類
          import?java.io.*;
          import?java.net.*;
          public?class?RemoteFileServer?{????
          ????int?listenPort;
          ????public?RemoteFileServer(int?listenPort)?{
          ????????this.listenPort=listenPort;
          ????}
          ????//允許客戶機(jī)連接到服務(wù)器,等待客戶機(jī)請求????
          ????public?void?acceptConnections()?{
          ????????try?{
          ????????????ServerSocket?server?=?new?ServerSocket(listenPort);
          ????????????Socket?incomingConnection?=?null;
          ????????????while(true)?{
          ????????????????incomingConnection?=?server.accept();
          ????????????????handleConnection(incomingConnection);
          ????????????}
          ????????}
          ????????catch(BindException?e)?{
          ????????????System.out.println("Unable?to?bind?to?port?"+listenPort);
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println("Unable?to?instantiate?a?ServerSocket?on?port:?"+listenPort);??
          ????????????
          ????????}
          ????}
          ????//與客戶機(jī)Socket交互以將客戶機(jī)所請求的文件的內(nèi)容發(fā)送到客戶機(jī)
          ????public?void?handleConnection(Socket?incomingConnection)?{
          ????????try?{
          ????????????OutputStream?outputToSocket?=?incomingConnection.getOutputStream();?
          ????????????InputStream?inputFromSocket?=?incomingConnection.getInputStream();
          ????????????BufferedReader?streamReader?=?new?BufferedReader(new?InputStreamReader(inputFromSocket));
          ????????????FileReader?fileReader?=?new?FileReader(new?File(streamReader.readLine()));
          ????????????BufferedReader?bufferedFileReader?=?new?BufferedReader(fileReader);?
          ????????????PrintWriter?streamWriter?=?new?PrintWriter(incomingConnection.getOutputStream());
          ????????????String?line?=?null;
          ????????????while((line=bufferedFileReader.readLine())!=null){
          ????????????????streamWriter.println(line);
          ????????????}
          ????????????fileReader.close();
          ????????????streamWriter.close();
          ????????????streamReader.close();
          ????????}
          ????????catch(Exception?e)?{
          ????????????System.out.println("Error?handling?a?client:?"+e);
          ????????????e.printStackTrace();?
          ????????}
          ????}
          ????public?static?void?main(String?args[])?{
          ????????RemoteFileServer?server?=?new?RemoteFileServer(1001);
          ????????server.acceptConnections();
          ????}
          }
          跟客戶機(jī)中一樣,我們首先導(dǎo)入java.net的java.io。接著,我們給我們的類一個實(shí)例變量以保存端口,我們從該端口偵聽進(jìn)入的連接。缺省情況下,端口是1001。
          我們的類有一個main()方法和兩個其它方法。稍后我們將探究這些方法的細(xì)節(jié)?,F(xiàn)在您只需知道acceptConnections()將允許客戶機(jī)連接到服務(wù)器以及handleConnection()與客戶機(jī)Socket交互以將您所請求的文件的內(nèi)容發(fā)送到客戶機(jī)。
          實(shí)現(xiàn)?main()
          這里我們實(shí)現(xiàn)main()方法,它將創(chuàng)建RemoteFileServer并告訴它接受連接:服務(wù)器端的main()方法中,我們實(shí)例化一個新RemoteFileServer,它將在偵聽端口(1001)上偵聽進(jìn)入的連接請求。然后我們調(diào)用acceptConnections()來告訴該server進(jìn)行偵聽。
          接受連接
          這里我們實(shí)現(xiàn)?acceptConnections()?方法,它將創(chuàng)建一個?ServerSocket?并等待連接請求:
          ????public?void?acceptConnections()?{
          ????????try?{
          ????????????ServerSocket?server?=?new?ServerSocket(listenPort);
          ????????????Socket?incomingConnection?=?null;
          ????????????while(true)?{
          ????????????????incomingConnection?=?server.accept();
          ????????????????handleConnection(incomingConnection);
          ????????????}
          ????????}
          ????????catch(BindException?e)?{
          ????????????System.out.println("Unable?to?bind?to?port?"+listenPort);
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println("Unable?to?instantiate?a?ServerSocket?on?port:?"+listenPort);??
          ????????????
          ????????}
          ????}
          acceptConnections()用欲偵聽的端口號來創(chuàng)建ServerSocket。然后我們通過調(diào)用該ServerSocket的accept()來告訴它開始偵聽。accept()方法將造成阻塞直到來了一個連接請求。此時(shí),accept()返回一個新的Socket,這個Socket綁定到服務(wù)器上一個隨機(jī)指定的端口,返回的Socket被傳遞給handleConnection()。請注意我們在一個無限循環(huán)中處理對連接的接受。這里不支持任何關(guān)機(jī)。
          無論何時(shí)如果您創(chuàng)建了一個無法綁定到指定端口(可能是因?yàn)閯e的什么控制了該端口)的?ServerSocket,Java代碼都將拋出一個錯誤。所以這里我們必須捕捉可能的BindException。就跟在客戶機(jī)端上時(shí)一樣,我們必須捕捉IOException,當(dāng)我們試圖在ServerSocket上接受連接時(shí),它就會被拋出。請注意,您可以通過用毫秒數(shù)調(diào)用setSoTimeout()來為accept()調(diào)用設(shè)置超時(shí),以避免實(shí)際長時(shí)間的等待。調(diào)用setSoTimeout()將使accept()經(jīng)過指定占用時(shí)間后拋出IOException。
          處理連接
          這里我們實(shí)現(xiàn)handleConnection()方法,它將用連接的流來接收輸入和寫輸出:
          ????public?void?handleConnection(Socket?incomingConnection)?{
          ????????try?{
          ????????????OutputStream?outputToSocket?=?incomingConnection.getOutputStream();?
          ????????????InputStream?inputFromSocket?=?incomingConnection.getInputStream();
          ????????????BufferedReader?streamReader?=?new?BufferedReader(new?InputStreamReader(inputFromSocket));
          ????????????FileReader?fileReader?=?new?FileReader(new?File(streamReader.readLine()));
          ????????????BufferedReader?bufferedFileReader?=?new?BufferedReader(fileReader);?
          ????????????PrintWriter?streamWriter?=?new?PrintWriter(incomingConnection.getOutputStream());
          ????????????String?line?=?null;
          ????????????while((line=bufferedFileReader.readLine())!=null){
          ????????????????streamWriter.println(line);
          ????????????}
          ????????????fileReader.close();
          ????????????streamWriter.close();
          ????????????streamReader.close();
          ????????}
          ????????catch(Exception?e)?{
          ????????????System.out.println("Error?handling?a?client:?"+e);
          ????????????e.printStackTrace();?
          ????????}
          ????}
          跟在客戶機(jī)中一樣,我們用getOutputStream()和getInputStream()來獲取與我們剛創(chuàng)建的Socket相關(guān)聯(lián)的流。跟在客戶機(jī)端一樣,我們把InputStream包裝進(jìn)BufferedReader,把OutputStream包裝進(jìn)PrintWriter。在服務(wù)器端上,我們需要添加一些代碼,用來讀取目標(biāo)文件和把內(nèi)容逐行發(fā)送到客戶機(jī)。這里是重要的代碼:
          ????FileReader?fileReader?=?new?FileReader(new?File(streamReader.readLine()));????BufferedReader?bufferedFileReader?=?new?BufferedReader(fileReader);????String?line?=?null;????while((line=bufferedFileReader.readLine())!=null)?{????????streamWriter.println(line);????}
          這些代碼值得詳細(xì)解釋。讓我們一點(diǎn)一點(diǎn)來看:
          ????FileReader?fileReader?=?new?FileReader(new?File(streamReader.readLine()));
          首先,我們使用Socket?的InputStream的BufferedReader。我們應(yīng)該獲取一條有效的文件路徑,所以我們用該路徑名構(gòu)造一個新File。我們創(chuàng)建一個新FileReader來處理讀文件的操作。
          ????BufferedReader?bufferedFileReader?=?new?BufferedReader(fileReader);
          這里我們把FileReader包裝進(jìn)BufferedReader以使我們能夠逐行地讀該文件。
          接著,我們調(diào)用BufferedReader的readLine()。這個調(diào)用將造成阻塞直到有字節(jié)到來。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后再寫出到客戶機(jī)上。完成讀寫操作之后,我們就關(guān)閉打開的流。
          請注意我們在完成從Socket的讀操作之后關(guān)閉streamWriter和streamReader。您或許會問我們?yōu)槭裁床辉谧x取文件名之后立刻關(guān)閉streamReader。原因是當(dāng)您這樣做時(shí),您的客戶機(jī)將不會獲取任何數(shù)據(jù)。如果您在關(guān)閉streamWriter之前關(guān)閉streamReader,則您可以往Socket寫任何東西,但卻沒有任何數(shù)據(jù)能通過通道(通道被關(guān)閉了)。
          總結(jié)一下服務(wù)器
          在我們接著討論另一個更實(shí)際的示例之前,讓我們回顧一下創(chuàng)建和使用ServerSocket的步驟:
          1.????用一個您想讓它偵聽傳入客戶機(jī)連接的端口來實(shí)例化一個ServerSocket(如有問題則拋出?Exception)。
          2.????調(diào)用ServerSocket的accept()以在等待連接期間造成阻塞。
          3.????獲取位于該底層Socket的流以進(jìn)行讀寫操作。
          4.????按使事情簡單化的原則包裝流。
          5.????對Socket進(jìn)行讀寫。
          6.????關(guān)閉打開的流(并請記住,永遠(yuǎn)不要在關(guān)閉Writer之前關(guān)閉Reader)。?
          6?創(chuàng)建多線程Socket服務(wù)器
          前面的示例教給您基礎(chǔ)知識,但并不能令您更深入。如果您到此就停止了,那么您一次只能處理一臺客戶機(jī)。原因是handleConnection()是一個阻塞方法。只有當(dāng)它完成了對當(dāng)前連接的處理時(shí),服務(wù)器才能接受另一個客戶機(jī)。在多數(shù)時(shí)候,您將需要(也有必要)一個多線程服務(wù)器。
          創(chuàng)建?MultithreadedRemoteFileServer?類
          import?java.io.*;
          import?java.net.*;
          public?class?MultithreadedRemoteFileServer?{
          ????int?listenPort;
          ????public?MultithreadedRemoteFileServer(int?listenPort)?{????????
          ????????this.listenPort=listenPort;
          ????}
          ????//允許客戶機(jī)連接到服務(wù)器,等待客戶機(jī)請求????
          ????public?void?acceptConnections()?{
          ????????try?{
          ????????????ServerSocket?server?=?new?ServerSocket(listenPort,?5);
          ????????????Socket?incomingConnection?=?null;
          ????????????while(true)?{
          ????????????????incomingConnection?=?server.accept();?
          ????????????????handleConnection(incomingConnection);
          ????????????}
          ????????}????????
          ????????catch(BindException?e)?{
          ????????????System.out.println("Unable?to?bind?to?port?"+listenPort);
          ????????}?
          ????????catch(IOException?e)?{????????????
          ????????????System.out.println("Unable?to?instantiate?a?ServerSocket?on?port:?"+listenPort);????????
          ????????}
          ????}
          ????//與客戶機(jī)Socket交互以將客戶機(jī)所請求的文件的內(nèi)容發(fā)送到客戶機(jī)????
          ????public?void?handleConnection(Socket?connectionToHandle)?{?
          ????????new?Thread(new?ConnectionHandler(connectionToHandle)).start();
          ????}
          ????public?static?void?main(String?args[])?{
          ????????MultithreadedRemoteFileServer?server?=?new?MultithreadedRemoteFileServer(1001);
          ????????server.acceptConnections();
          ????}
          }
          這里我們實(shí)現(xiàn)改動過acceptConnections()方法,它將創(chuàng)建一個能夠處理待發(fā)請求的ServerSocket,并告訴ServerSocket接受連接。
          新的?server?仍然需要acceptConnections(),所以這些代碼實(shí)際上是一樣的。突出顯示的行表示一個重大的不同。對這個多線程版,我們現(xiàn)在可以指定客戶機(jī)請求的最大數(shù)目,這些請求都能在實(shí)例化ServerSocket期間處于待發(fā)狀態(tài)。如果我們沒有指定客戶機(jī)請求的最大數(shù)目,則我們假設(shè)使用缺省值50。
          這里是它的工作機(jī)制。假設(shè)我們指定待發(fā)數(shù)(backlog?值)是5并且有五臺客戶機(jī)請求連接到我們的服務(wù)器。我們的服務(wù)器將著手處理第一個連接,但處理該連接需要很長時(shí)間。由于我們的待發(fā)值是5,所以我們一次可以放五個請求到隊(duì)列中。我們正在處理一個,所以這意味著還有其它五個正在等待。等待的和正在處理的一共有六個。當(dāng)我們的服務(wù)器仍忙于接受一號連接(記住隊(duì)列中還有?2?6?號)時(shí),如果有第七個客戶機(jī)提出連接申請,那么,該第七個客戶機(jī)將遭到拒絕。我們將在帶有連接池服務(wù)器示例中說明如何限定能同時(shí)連接的客戶機(jī)數(shù)目。
          處理連接:?
          ????public?void?handleConnection(Socket?connectionToHandle)?{?
          ????????new?Thread(new?ConnectionHandler(connectionToHandle)).start();
          ????}
          我們對RemoteFileServer所做的大改動就體現(xiàn)在這個方法上。我們?nèi)匀辉诜?wù)器接受一個連接之后調(diào)用handleConnection(),但現(xiàn)在我們把該Socket傳遞給ConnectionHandler的一個實(shí)例,它是?Runnable的。我們用ConnectionHandler創(chuàng)建一個新?Thread?并啟動它。ConnectionHandler的run()方法包Socket讀/寫和讀File的代碼,這些代碼原來在RemoteFileServer的handleConnection()中。
          創(chuàng)建?ConnectionHandler?類
          import?java.io.*;
          import?java.net.*;
          public?class?ConnectionHandler?implements?Runnable?{
          ????protected?Socket?socketToHandle;
          ????public?ConnectionHandler(Socket?socketToHandle)?{
          ????????this.socketToHandle=socketToHandle;
          ????}
          ????public?void?run()?{
          ????????try?{
          ????????????PrintWriter?streamWriter?=?new?PrintWriter(socketToHandle.getOutputStream());?
          ????????????BufferedReader?streamReader?=?new?BufferedReader(new?InputStreamReader(socketToHandle.getInputStream()));
          ????????????String?fileToRead?=?streamReader.readLine();
          ????????????BufferedReader?fileReader?=?new?BufferedReader(new?FileReader(fileToRead));?
          ????????????String?line?=null;
          ????????????while((line=fileReader.readLine())!=null)?{
          ????????????????streamWriter.println(line);
          ????????????}
          ????????????fileReader.close();
          ????????????streamWriter.close();
          ????????????streamReader.close();
          ????????}
          ????????catch(Exception?e)?{
          ????????????System.out.println("Error?handling?a?client:?"+e);
          ????????e.printStackTrace();
          ????????}
          ????}
          }
          這個助手類相當(dāng)簡單。跟我們到目前為止的其它類一樣,我們導(dǎo)入java.net和java.io。該類只有一個實(shí)例變量socketToHandle,它保存由該實(shí)例處理的Socket。
          類的構(gòu)造器用一個Socket實(shí)例作參數(shù)并將它賦給socketToHandle。
          請注意該類實(shí)現(xiàn)了Runnable接口。實(shí)現(xiàn)這個接口的類都必須實(shí)現(xiàn)run()方法。這里我們實(shí)現(xiàn)run()方法,它將攫取我們的連接的流,用它來讀寫該連接,并在任務(wù)完成之后關(guān)閉它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我們把InputStream和OutputStream分別包裝(用Socket的getOutputStream()和?getInputStream())進(jìn)BufferedReader和PrintWriter。然后我們用這些代碼逐行地讀目標(biāo)文件:
          PrintWriter?streamWriter?=?new?PrintWriter(socketToHandle.getOutputStream());?
          ????????????BufferedReader?streamReader?=?new?BufferedReader(new?InputStreamReader(socketToHandle.getInputStream()));
          ????????????String?fileToRead?=?streamReader.readLine();
          ????????????BufferedReader?fileReader?=?new?BufferedReader(new?FileReader(fileToRead));?
          ????????????String?line?=null;
          ????????????while((line=fileReader.readLine())!=null)?{
          ????????????????streamWriter.println(line);
          ????????????}
          請記住我們應(yīng)該從客戶機(jī)獲取一條有效的文件路徑,這樣用該路徑名構(gòu)造一個新File,把它包裝進(jìn)FileReader以處理讀文件的操作,然后把它包裝進(jìn)BufferedReader以讓我們逐行地讀該文件。我們while循環(huán)中調(diào)用BufferedReader上的readLine()直到不再有要讀的行。請記注,對readLine()的調(diào)用將造成阻塞,直到有字節(jié)來到為止。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后寫出到客戶機(jī)上。完成讀寫操作之后,我們關(guān)閉打開的流。
          總結(jié)一下多線程服務(wù)器
          讓我們回顧一下創(chuàng)建和使用“多線程版”的服務(wù)器的步驟:
          1.????修改?acceptConnections()?以用缺省為?50(或任何您想要的大于?1?的指定數(shù)字)實(shí)例化?ServerSocket。
          2.????修改?ServerSocket?的?handleConnection()?以用?ConnectionHandler?的一個實(shí)例生成一個新的?Thread。
          3.????借用?RemoteFileServer?的?handleConnection()?方法的代碼實(shí)現(xiàn)?ConnectionHandler?類。?
          7?創(chuàng)建帶有連接池的Socket服務(wù)器
          我們現(xiàn)在已經(jīng)擁有的?MultithreadedServer?每當(dāng)有客戶機(jī)申請一個連接時(shí)都在一個新Thread中創(chuàng)建一個新ConnectionHandler。這意味著可能有一捆Thread“躺”在我們周圍。而且創(chuàng)建Thread的系統(tǒng)開銷并不是微不足道的。如果性能成為了問題(也請不要事到臨頭才意識到它),更高效地處理我們的服務(wù)器是件好事。那么,我們?nèi)绾胃咝У毓芾矸?wù)器端呢?我們可以維護(hù)一個進(jìn)入的連接池,一定數(shù)量的ConnectionHandler將為它提供服務(wù)。這種設(shè)計(jì)能帶來以下好處:
          ?????它限定了允許同時(shí)連接的數(shù)目。?
          ?????我們只需啟動ConnectionHandler?Thread一次。?
          幸運(yùn)的是,跟在我們的多線程示例中一樣,往代碼中添加“池”不需要來一個大改動。事實(shí)上,應(yīng)用程序的客戶機(jī)端根本就不受影響。在服務(wù)器端,我們在服務(wù)器啟動時(shí)創(chuàng)建一定數(shù)量的?ConnectionHandler,我們把進(jìn)入的連接放入“池”中并讓ConnectionHandler打理剩下的事情。這種設(shè)計(jì)中有很多我們不打算討論的可能存在的技巧。例如,我們可以通過限定允許在“池”中建立的連接的數(shù)目來拒絕客戶機(jī)。
          請注意:我們將不會再次討論acceptConnections()。這個方法跟前面示例中的完全一樣。它無限循環(huán)地調(diào)用ServerSocket上的?accept()?并把連接傳遞到handleConnection()。
          創(chuàng)建?PooledRemoteFileServer?類
          import?java.io.*;
          import?java.net.*;
          import?java.util.*;
          public?class?PooledRemoteFileServer?{
          ????protected?int?maxConnections;
          ????protected?int?listenPort;
          ????protected?ServerSocket?serverSocket;
          ????public?PooledRemoteFileServer(int?aListenPort,?int?maxConnections)?{
          ????????listenPort=?aListenPort;
          ????????this.maxConnections?=?maxConnections;
          ????}
          ????public?void?acceptConnections()?{
          ????????try?{
          ????????????ServerSocket?server?=?new?ServerSocket(listenPort,?5);
          ????????????Socket?incomingConnection?=?null;
          ????????????while(true)?{
          ????????????????incomingConnection?=?server.accept();
          ????????????????handleConnection(incomingConnection);
          ????????????}
          ????????}
          ????????catch(BindException?e)?{
          ????????????System.out.println("");
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println(""+listenPort);
          ????????}
          ????}
          ????protected?void?handleConnection(Socket?connectionToHandle)?{
          ????????PooledConnectionHandler.processRequest(connectionToHandle);
          ????}
          ????public?void?setUpHandlers()?{
          ????????for(int?i=0;?i<maxConnections;?i++)?{
          ????????????PooledConnectionHandler?currentHandler?=?new?PooledConnectionHandler();
          ????????????new?Thread(currentHandler,?"Handler?"?+?i).start();
          ????????}
          ????}
          ????public?static?void?main(String?args[])?{
          ????????PooledRemoteFileServer?server?=?new?PooledRemoteFileServer(1001,?3);
          ????????server.setUpHandlers();?
          ????????server.acceptConnections();
          ????}
          }
          請注意一下您現(xiàn)在應(yīng)該熟悉了的?import?語句。我們給類以下實(shí)例變量以保存:
          ?????我們的服務(wù)器能同時(shí)處理的活動客戶機(jī)連接的最大數(shù)目
          ?????進(jìn)入的連接的偵聽端口(我們沒有指定缺省值,但如果您想這樣做,并不會受到限制)
          ?????將接受客戶機(jī)連接請求的?ServerSocket?
          類的構(gòu)造器用的參數(shù)是偵聽端口和連接的最大數(shù)目
          我們的類有一個?main()?方法和三個其它方法。稍后我們將探究這些方法的細(xì)節(jié)?,F(xiàn)在只須知道setUpHandlers()創(chuàng)建數(shù)目為maxConnections的大量PooledConnectionHandler,而其它兩個方法則與我們前面已經(jīng)看到的相似:acceptConnections()在ServerSocket上偵聽傳入的客戶機(jī)連接,而handleConnection則在客戶機(jī)連接一旦被建立后就實(shí)際處理它。
          實(shí)現(xiàn)?main()
          這里我們實(shí)現(xiàn)需作改動的main()方法,該方法將創(chuàng)建能夠處理給定數(shù)目的客戶機(jī)連接的PooledRemoteFileServer,并告訴它接受連接:
          ????public?static?void?main(String?args[])?{
          ????????PooledRemoteFileServer?server?=?new?PooledRemoteFileServer(1001,?3);
          ????????server.setUpHandlers();?
          ????????server.acceptConnections();
          ????}
          我們的main()方法很簡單。我們實(shí)例化一個新的PooledRemoteFileServer,它將通過調(diào)用setUpHandlers()來建立三個PooledConnectionHandler。一旦服務(wù)器就緒,我們就告訴它acceptConnections()。
          建立連接處理程序
          ???public?void?setUpHandlers()?{
          ????????for(int?i=0;?i<maxConnections;?i++)?{
          ????????????PooledConnectionHandler?currentHandler?=?new?PooledConnectionHandler();
          ????????????new?Thread(currentHandler,?"Handler?"?+?i).start();
          ????????}
          ????}
          setUpHandlers()方法創(chuàng)建maxConnections(例如?3)個PooledConnectionHandler并在新Thread中激活它們。用實(shí)現(xiàn)了Runnable的對象來創(chuàng)建Thread使我們可以在Thread調(diào)用start()并且可以期望在Runnable上調(diào)用了run()。換句話說,我們的PooledConnectionHandler將等著處理進(jìn)入的連接,每個都在它自己的Thread中進(jìn)行。我們在示例中只創(chuàng)建三個Thread,而且一旦服務(wù)器運(yùn)行,這就不能被改變。
          處理連接
          這里我們實(shí)現(xiàn)需作改動的handleConnections()方法,它將委派PooledConnectionHandler處理連接:
          ????protected?void?handleConnection(Socket?connectionToHandle)?{
          ????????PooledConnectionHandler.processRequest(connectionToHandle);
          ????}
          我們現(xiàn)在叫?PooledConnectionHandler?處理所有進(jìn)入的連接(processRequest()?是一個靜態(tài)方法)。
          創(chuàng)建?PooledRemoteFileServer?類
          import?java.io.*;
          import?java.net.*;
          import?java.util.*;
          public?class?PooledConnectionHandler?implements?Runnable?{
          ????protected?Socket?connection;
          ????protected?static?List?pool?=?new?LinkedList();
          ????public?PooledConnectionHandler()?{}
          ????public?void??handleConnection()?{
          ????????try?{
          ????????????PrintWriter?streamWriter?=?new?PrintWriter(connection.getOutputStream());?
          ????????????BufferedReader?streamReader?=?new?BufferedReader(new?InputStreamReader(connection.getInputStream()));?
          ????????????String?fileToRead?=?streamReader.readLine();
          ????????????BufferedReader?fileReader?=?new?BufferedReader(new?FileReader(fileToRead));
          ????????????String?line?=?null;
          ????????????while((line=fileReader.readLine())!=null)
          ????????????????streamWriter.println(line);?
          ????????????fileReader.close();
          ????????????streamWriter.close();
          ????????????streamReader.close();
          ????????}
          ????????catch(FileNotFoundException?e)?{
          ????????????System.out.println("");
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println(""+e);
          ????????}
          ????}
          ????public?static?void?processRequest(Socket?requestToHandle)?{
          ????????synchronized(pool)?{
          ????????????pool.add(pool.size(),?requestToHandle);
          ????????????pool.notifyAll();
          ????????}
          ????}
          ????public?void?run()?{
          ????????while(true)?{
          ????????????synchronized(pool)?{
          ????????????????while(pool.isEmpty())?{
          ????????????????????try?{
          ????????????????????????pool.wait();
          ????????????????????}
          ????????????????????catch(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();?
          ????????????????????}
          ????????????????}
          ????????????????connection=?(Socket)pool.remove(0);?
          ????????????}
          ????????????handleConnection();
          ????????}
          ????}
          }
          這個助手類與?ConnectionHandler?非常相似,但它帶有處理連接池的手段。該類有兩個實(shí)例變量:
          ?????connection?是當(dāng)前正在處理的?Socket?
          ?????名為?pool?的靜態(tài)?LinkedList?保存需被處理的連接?
          填充連接池
          這里我們實(shí)現(xiàn)PooledConnectionHandler上的processRequest()方法,它將把傳入請求添加到池中,并告訴其它正在等待的對象該池已經(jīng)有一些內(nèi)容:
          ????public?static?void?processRequest(Socket?requestToHandle)?{
          ????????synchronized(pool)?{
          ????????????pool.add(pool.size(),?requestToHandle);
          ????????????pool.notifyAll();
          ????????}
          ????}
          synchronized?塊是個稍微有些不同的東西。您可以同步任何對象上的一個塊,而不只是在本身的某個方法中含有該塊的對象。在我們的示例中,processRequest()?方法包含有一個?pool(請記住它是一個?LinkedList,保存等待處理的連接池)的?synchronized塊。我們這樣做的原因是確保沒有別人能跟我們同時(shí)修改連接池。
          既然我們已經(jīng)保證了我們是唯一“涉水”池中的人,我們就可以把傳入的Socket添加到LinkedList的尾端。一旦我們添加了新的連接,我們就用以下代碼通知其它正在等待該池的Thread,池現(xiàn)在已經(jīng)可用:
          ????pool.notifyAll();
          Object的所有子類都繼承這個notifyAll()方法。這個方法,連同我們下一屏將要討論的wait()方法一起,就使一個Thread能夠讓另一個Thread知道一些條件已經(jīng)具備。這意味著該第二個Thread一定正在等待那些條件的滿足。
          從池中獲取連接
          這里我們實(shí)現(xiàn)PooledConnectionHandler上需作改動的run()方法,它將在連接池上等待,并且池中一有連接就處理它:
          ????public?void?run()?{
          ????????while(true)?{
          ????????????synchronized(pool)?{
          ????????????????while(pool.isEmpty())?{
          ????????????????????try?{
          ????????????????????????pool.wait();
          ????????????????????}
          ????????????????????catch(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();?
          ????????????????????}
          ????????????????}
          ????????????????connection=?(Socket)pool.remove(0);?
          ????????????}
          ????????????handleConnection();
          ????????}
          ????}
          回想一下在前面講過的:一個Thread正在等待有人通知它連接池方面的條件已經(jīng)滿足了。在我們的示例中,請記住我們有三個PooledConnectionHandler在等待使用池中的連接。每個PooledConnectionHandler都在它自已的Thread中運(yùn)行,并通過調(diào)用pool.wait()產(chǎn)生阻塞。當(dāng)我們的processRequest()在連接池上調(diào)用notifyAll()時(shí),所有正在等待的PooledConnectionHandler都將得到“池已經(jīng)可用”的通知。然后各自繼續(xù)前行調(diào)用pool.wait(),并重新檢查while(pool.isEmpty())循環(huán)條件。除了一個處理程序,其它池對所有處理程序都將是空的,因此,在調(diào)用pool.wait()時(shí),除了一個處理程序,其它所有處理程序都將再次產(chǎn)生阻塞。恰巧碰上非空池的處理程序?qū)⑻鰓hile(pool.isEmpty())循環(huán)并攫取池中的第一個連接:
          ????connection=?(Socket)pool.remove(0);
          處理程序一旦有一個連接可以使用,就調(diào)用?handleConnection()?處理它。
          在我們的示例中,池中可能永遠(yuǎn)不會有多個連接,只是因?yàn)槭虑楹芸炀捅惶幚淼袅?。如果池中有一個以上連接,那么其它處理程序?qū)⒉槐氐却碌倪B接被添加到池。當(dāng)它們檢查pool.isEmpty()條件時(shí),將發(fā)現(xiàn)其值為假,然后就從池中攫取一個連接并處理它。
          還有另一件事需注意。當(dāng)run()擁有池的互斥鎖時(shí),processRequest()如何能夠把連接放到池中呢?答案是對池上的wait()的調(diào)用釋放鎖,而wait()接著就在自己返回之前再次攫取該鎖。這就使得池對象的其它同步代碼可以獲取該鎖。
          處理連接:再一次
          這里我們實(shí)現(xiàn)需做改動的handleConnection()方法,該方法將攫取連接的流,使用它們,并在任務(wù)完成之后清除它們:
          ????public?void??handleConnection()?{
          ????????try?{
          ????????????PrintWriter?streamWriter?=?new?PrintWriter(connection.getOutputStream());?
          ????????????BufferedReader?streamReader?=?new?BufferedReader(new?InputStreamReader(connection.getInputStream()));?
          ????????????String?fileToRead?=?streamReader.readLine();
          ????????????BufferedReader?fileReader?=?new?BufferedReader(new?FileReader(fileToRead));
          ????????????String?line?=?null;
          ????????????while((line=fileReader.readLine())!=null)
          ????????????????streamWriter.println(line);?
          ????????????fileReader.close();
          ????????????streamWriter.close();
          ????????????streamReader.close();
          ????????}
          ????????catch(FileNotFoundException?e)?{
          ????????????System.out.println("");
          ????????}
          ????????catch(IOException?e)?{
          ????????????System.out.println(""+e);
          ????????}
          ????}
          跟在多線程服務(wù)器中不同,我們的PooledConnectionHandler有一個handleConnection()方法。這個方法的代碼跟非池式的ConnectionHandler上的run()方法的代碼完全一樣。首先,我們把OutputStream和InputStream分別包裝進(jìn)(用Socket上的getOutputStream()和getInputStream())BufferedReader和PrintWriter。然后我們逐行讀目標(biāo)文件,就象我們在多線程示例中做的那樣。再一次,我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后寫出到客戶機(jī)。完成讀寫操作之后,我們關(guān)閉FileReader和打開的流。
          總結(jié)一下帶有連接池的服務(wù)器
          讓我們回顧一下創(chuàng)建和使用“池版”服務(wù)器的步驟:
          1.????創(chuàng)建一個新種類的連接處理程序(我們稱之為?PooledConnectionHandler)來處理池中的連接。
          2.????修改服務(wù)器以創(chuàng)建和使用一組?PooledConnectionHandler。?

          Java?語言簡化了套接字在應(yīng)用程序中的使用。它的基礎(chǔ)實(shí)際上是?java.net?包中的?Socket?和?ServerSocket?類。一旦您理解了表象背后發(fā)生的情況,就能容易地使用這些類。在現(xiàn)實(shí)生活中使用套接字只是這樣一件事,即通過貫徹優(yōu)秀的?OO?設(shè)計(jì)原則來保護(hù)應(yīng)用程序中各層間的封裝。我們?yōu)槟故玖艘恍┯袔椭念?。這些類的結(jié)構(gòu)對我們的應(yīng)用程序隱藏了?Socket?交互作用的低級細(xì)節(jié)???使應(yīng)用程序能只使用可插入的?ClientSocketFacade?和?ServerSocketFacade。在有些地方(在?Facade?內(nèi)),您仍然必須管理稍顯雜亂的字節(jié)細(xì)節(jié),但您只須做一次就可以了。更好的是,您可以在將來的項(xiàng)目中重用這些低級別的助手類。


          doc格式文件
          附件:Java_Socket.doc(121K)?


          Java?范型攻略篇
          yongbing 整理???更新:2006-12-08 21:37:43??版本: 1.0 ??

          在已發(fā)布的Java1.4中在核心代碼庫中增加了許多新的API(如Loging,正則表達(dá)式,NIO)等,在最新發(fā)布的JDK1.5和即將發(fā)布的JDK1.6中也新增了許多API,其中比較有重大意義的就是Generics(范型)。

          一.什么是Generics?

          Generics可以稱之為參數(shù)類型(parameterized?types),由編譯器來驗(yàn)證從客戶端將一種類型傳送給某一對象的機(jī)制。如Java.util.ArrayList,

          編譯器可以用Generics來保證類型安全。
          在我們深入了解Generics之前,我們先來看一看當(dāng)前的java?集合框架(Collection)。在j2SE1.4中所有集合的Root?Interface是Collection

          Collections?example?without?genericity:?Example?1

          1?protected?void?collectionsExample()?{
          2??ArrayList?list?=?new?ArrayList();
          3??list.add(new?String("test?string"));
          4??list.add(new?Integer(9));?//?purposely?placed?here?to?create?a?runtime?ClassCastException
          5??inspectCollection(list);
          6?}
          7
          8
          9?protected?void?inspectCollection(Collection?aCollection)?{
          10??Iterator?i?=?aCollection.iterator();
          11??while?(i.hasNext())?{
          12???String?element?=?(String)?i.next();
          13??}
          14?}


          以上的樣例程序包含的兩個方法,collectionExample方法建立了一個簡單的集合類型ArrayList,并在ArrayList中增加了一個String和一個Integer對象.而在inspecCollection方法中,我們迭代這個ArrayList用String進(jìn)行Cast。我們看第二個方法,就出現(xiàn)了一個問題,Collection在內(nèi)部用的是Object,而我們要取出Collection中的對象時(shí),需要進(jìn)行Cast,那么開發(fā)者必需用實(shí)際的類型進(jìn)行Cast,像這種向下造型,編譯器無

          法進(jìn)行檢查,如此一來我們就要冒在代碼在運(yùn)行拋出ClassCastException的危險(xiǎn)。我們看inspecCollection方法,編譯時(shí)沒有問題,但在運(yùn)行時(shí)就會拋出ClassCastException異常。所以我們一定要遠(yuǎn)離這個重大的運(yùn)行時(shí)錯誤


          二.使用Generics
          從上一章節(jié)中的CassCastException這種異常,我們期望在代碼編譯時(shí)就能夠捕捉到,下面我們使用范型修改上一章的樣例程序。
          //Example?2
          1?protected?void?collectionsExample()?{
          2??ArrayList<String>?list?=?new?ArrayList<String>();
          3??list.add(new?String("test?string"));
          4??//?list.add(new?Integer(9));?this?no?longer?compiles
          5??inspectCollection(list);
          6?}
          7?
          8?
          9?protected?void?inspectCollection(Collection<String>?aCollection)?{
          10??Iterator<String>?i?=?aCollection.iterator();
          11??while(i.hasNext())?{
          12???String?element?=?i.next();
          13??}
          14?}


          從上面第2行我們在創(chuàng)建ArrayList時(shí)使用了新語法,在JDK1.5中所有的Collection都加入了Generics的聲明。例:
          //Example?3
          1?public?class?ArrayList<E>?extends?AbstractList<E>?{
          2??//?details?omitted...
          3??public?void?add(E?element)?{
          4???//?details?omitted
          5??}
          6??public?Iterator<E>?iterator()?{
          7???//?details?omitted
          8??}
          9?}


          這個E是一個類型變量,并沒有對它進(jìn)行具體類型的定義,它只是在定義ArrayList時(shí)的類型占位符,在Example?2中的我們在定義ArrayList的實(shí)

          例時(shí)用String綁定在E上,當(dāng)我們用add(E?element)方法向ArrayList中增加對象時(shí),?那么就像下面的寫法一樣:?public?void?add(String?element);因?yàn)樵贏rrayList所有方法都會用String來替代E,無論是方法的參數(shù)還是返回值。這時(shí)我們在看Example?2中的第四行,編譯就會反映出編譯錯誤。
          所以在java中增加Generics主要的目的是為了增加類型安全。

          通過上面的簡單的例子我們看到使用Generics的好處有:
          1.在類型沒有變化時(shí),Collection是類型安全的。
          2.內(nèi)在的類型轉(zhuǎn)換優(yōu)于在外部的人工造型。
          3.使Java?接口更加強(qiáng)壯,因?yàn)樗黾恿祟愋汀?br />4.類型的匹配錯誤在編譯階段就可以捕捉到,而不是在代碼運(yùn)行時(shí)。

          受約束類型變量
          雖然許多Class被設(shè)計(jì)成Generics,但類型變量可以是受限的
          public?class?C1<T?extends?Number>?{?}
          public?class?C2<T?extends?Person?&?Comparable>?{?}?
          第一個T變量必須繼承Number,第二個T必須繼承Person和實(shí)現(xiàn)Comparable

          三.Generics?方法

          像Generics類一樣,方法和構(gòu)造函數(shù)也可以有類型參數(shù)。方法的參數(shù)的返回值都可以有類型參數(shù),進(jìn)行Generics。
          //Example?4
          1?public?<T?extends?Comparable>?T?max(T?t1,?T?t2)?{
          2??if?(t1.compareTo(t2)?>?0)
          3???return?t1;
          4??else?return?t2;
          5?}


          這里,max方法的參數(shù)類型為單一的T類型,而T類型繼承了Comparable,max的參數(shù)和返回值都有相同的超類。下面的Example?5顯示了max方法的幾個約束。
          //Example?5 
          1?Integer?iresult?=?max(new?Integer(100),?new?Integer(200));
          2?String?sresult?=?max("AA",?"BB");
          3?Number?nresult?=?max(new?Integer(100),?"AAA");?//?does?not?compile


          在Example?5第1行參數(shù)都為Integer,所以返回值也是Integer,注意返回值沒有進(jìn)行造型。
          在Example?5第2行參數(shù)都為String,所以返回值也是String,注意返回值沒有進(jìn)行造型。以上都調(diào)用了同一個方法。
          在Example?5第3行產(chǎn)生以下編譯錯誤:
          Example.java:10:?incompatible?types
          found??:?java.lang.Object&java.io.Serializable&java.lang.Comparable<?>
          required:?java.lang.Number
          ????Number?nresult?=?max(new?Integer(100),?"AAA");

          這個錯誤發(fā)生是因?yàn)榫幾g器無法確定返回值類型,因?yàn)镾tring和Integer都有相同的超類Object,注意就算我們修正了第三行,這行代碼在運(yùn)行仍然會報(bào)錯,因?yàn)楸容^了不同的對象。

          四.向下兼容
          任何一個新的特色在新的JDK版本中出來后,我們首先關(guān)心的是如何于以前編寫的代碼兼容。也就是說我們編寫的Example?1程序不需要任何的改變就可以運(yùn)行,但是編譯器會給出一個"ROW?TYPE"的警告。在JDK1.4中編寫的代碼如何在JVM1.5中完全兼容運(yùn)行,我們要人工進(jìn)行一個:Type?erasure處理過程

          五.通配符

          //Example?6
          List<String>?stringList?=?new?ArrayList<String>();?//1
          List<Object>?objectList?=?stringList?;//2
          objectList?.add(new?Object());?//?3
          String?s?=?stringList?.get(0);//4


          乍一看,Example?

          6是正確的。但stringList本意是存放String類型的ArrayList,而objectList中可以存入任何對象,當(dāng)在第3行進(jìn)行處理時(shí),stringList也就無法保證是String類型的ArrayList,此時(shí)編譯器不允許這樣的事出現(xiàn),所以第3行將無法編譯。

          //Example?7
          void?printCollection(Collection<Object>?c)?
          {?for?(Object?e?:?c)?{?
          System.out.println(e);
          }}


          Example?7的本意是打印所有Collection的對象,但是正如Example?6所說的,編譯會報(bào)錯,此時(shí)就可以用通配符“?”來修改Example?7

          //Example?8
          void?printCollection(Collection<?>?c)?
          {?for?(Object?e?:?c)?{?
          System.out.println(e);
          }}


          Example?8中所有Collection類型就可以方便的打印了

          有界通配符?<T?extends?Number>(上界)?<T?super?Number>(下界)?

          六.創(chuàng)建自己的范型
          以下代碼來自http://www.java2s.com/ExampleCode/Language-Basics
          1.一個參數(shù)的Generics
          //Example?9(沒有使用范型)
          class?NonGen?{??
          ??Object?ob;?//?ob?is?now?of?type?Object?
          ??//?Pass?the?constructor?a?reference?to???
          ??//?an?object?of?type?Object?
          ??NonGen(Object?o)?{??
          ????ob?=?o;??
          ??}??
          ??//?Return?type?Object.?
          ??Object?getob()?{??
          ????return?ob;??
          ??}??
          ??//?Show?type?of?ob.??
          ??void?showType()?{??
          ????System.out.println("Type?of?ob?is?"?+??
          ???????????????????????ob.getClass().getName());??
          ??}??
          }??
          //?Demonstrate?the?non-generic?class.??
          public?class?NonGenDemo?{??
          ??public?static?void?main(String?args[])?{??
          ????NonGen?iOb;???
          ????//?Create?NonGen?Object?and?store?
          ????//?an?Integer?in?it.?Autoboxing?still?occurs.?
          ????iOb?=?new?NonGen(88);??
          ????//?Show?the?type?of?data?used?by?iOb.?
          ????iOb.showType();?
          ????//?Get?the?value?of?iOb.?
          ????//?This?time,?a?cast?is?necessary.?
          ????int?v?=?(Integer)?iOb.getob();??
          ????System.out.println("value:?"?+?v);??
          ????System.out.println();??
          ????//?Create?another?NonGen?object?and??
          ????//?store?a?String?in?it.?
          ????NonGen?strOb?=?new?NonGen("Non-Generics?Test");??
          ????//?Show?the?type?of?data?used?by?strOb.?
          ????strOb.showType();?
          ????//?Get?the?value?of?strOb.?
          ????//?Again,?notice?that?a?cast?is?necessary.??
          ????String?str?=?(String)?strOb.getob();??
          ????System.out.println("value:?"?+?str);??
          ????//?This?compiles,?but?is?conceptually?wrong!?
          ????iOb?=?strOb;?
          ????v?=?(Integer)?iOb.getob();?//?runtime?error!?
          ??}??
          }
          ??

          //Example?10(使用范型)
          class?Example1<T>{
          ?private?T?t;
          ?Example1(T?o){
          ??this.t=o;
          ??}
          ?T?getOb(){
          ??return?t;
          ?}
          ?void?ShowObject(){
          ??System.out.println("對象的類型是:"+t.getClass().getName());
          ?}
          }
          public?class?GenericsExample1?{

          ?/**
          ??*?@param?args
          ??*/
          ?public?static?void?main(String[]?args)?{
          ??//?TODO?Auto-generated?method?stub
          ??Example1<Integer>?examplei=new?Example1<Integer>(100);
          ??examplei.ShowObject();
          ??System.out.println("對象是:"+examplei.getOb());
          ??Example1<String>?examples=new?Example1<String>("Bill");
          ??examples.ShowObject();
          ??System.out.println("對象是:"+examples.getOb());
          ?}

          }


          我們來看Example?9沒有使用范型,所以我們需要進(jìn)行造型,而Example?10我們不需要任何的造型

          2.二個參數(shù)的Generics

          //Example?11
          class?TwoGen<T,?V>?{?
          ???T?ob1;?
          ???V?ob2;?
          ???//?Pass?the?constructor?a?reference?to??
          ???//?an?object?of?type?T.?
          ???TwoGen(T?o1,?V?o2)?{?
          ?????ob1?=?o1;?
          ?????ob2?=?o2;?
          ???}?
          ???//?Show?types?of?T?and?V.?
          ???void?showTypes()?{?
          ?????System.out.println("Type?of?T?is?"?+?
          ????????????????????????ob1.getClass().getName());?
          ?????System.out.println("Type?of?V?is?"?+?
          ????????????????????????ob2.getClass().getName());?
          ???}?
          ???T?getob1()?{?
          ?????return?ob1;?
          ???}?
          ???V?getob2()?{?
          ?????return?ob2;?
          ???}?
          ?}?

          public?class?GenericsExampleByTwoParam?{

          ?/**
          ??*?@param?args
          ??*/
          ?public?static?void?main(String[]?args)?{
          ??//?TODO?Auto-generated?method?stub
          ??TwoGen<Integer,?String>?tgObj?=?
          ???????new?TwoGen<Integer,?String>(88,?"Generics");?
          ?????//?Show?the?types.?
          ?????tgObj.showTypes();?
          ?????//?Obtain?and?show?values.?
          ?????int?v?=?tgObj.getob1();?
          ?????System.out.println("value:?"?+?v);?
          ?????String?str?=?tgObj.getob2();?
          ?????System.out.println("value:?"?+?str);?
          ???}?

          ?}


          3.Generics的Hierarchy

          //Example?12
          class?Stats<T?extends?Number>?{??
          ???T[]?nums;?//?array?of?Number?or?subclass?
          ???//?Pass?the?constructor?a?reference?to???
          ???//?an?array?of?type?Number?or?subclass.?
          ???Stats(T[]?o)?{??
          ?????nums?=?o;??
          ???}??
          ???//?Return?type?double?in?all?cases.?
          ???double?average()?{??
          ?????double?sum?=?0.0;?
          ?????for(int?i=0;?i?<?nums.length;?i++)??
          ???????sum?+=?nums[i].doubleValue();?
          ?????return?sum?/?nums.length;?
          ???}??
          ?}??
          public?class?GenericsExampleByHierarchy?{
          ?

          ?/**
          ??*?@param?args
          ??*/
          ?public?static?void?main(String[]?args)?{
          ??//?TODO?Auto-generated?method?stub

          ???Integer?inums[]?=?{?1,?2,?3,?4,?5?};?
          ?????Stats<Integer>?iob?=?new?Stats<Integer>(inums);???
          ?????double?v?=?iob.average();?
          ?????System.out.println("iob?average?is?"?+?v);?
          ?????Double?dnums[]?=?{?1.1,?2.2,?3.3,?4.4,?5.5?};?
          ?????Stats<Double>?dob?=?new?Stats<Double>(dnums);???
          ?????double?w?=?dob.average();?
          ?????System.out.println("dob?average?is?"?+?w);?
          ?????//?This?won't?compile?because?String?is?not?a?
          ?????//?subclass?of?Number.?
          //?????String?strs[]?=?{?"1",?"2",?"3",?"4",?"5"?};?
          //?????Stats<String>?strob?=?new?Stats<String>(strs);???
          //?????double?x?=?strob.average();?
          //?????System.out.println("strob?average?is?"?+?v);?
          ???}??
          ?}
          ??

          4.使用通配符
          //Example?14
          class?StatsWildCard<T?extends?Number>?{
          ?T[]?nums;?//?array?of?Number?or?subclass
          ?//?Pass?the?constructor?a?reference?to
          ?//?an?array?of?type?Number?or?subclass.
          ?StatsWildCard(T[]?o)?{
          ??nums?=?o;
          ?}
          ?//?Return?type?double?in?all?cases.
          ?double?average()?{
          ??double?sum?=?0.0;
          ??for?(int?i?=?0;?i?<?nums.length;?i++)
          ???sum?+=?nums[i].doubleValue();
          ??return?sum?/?nums.length;
          ?}
          ?//?Determine?if?two?averages?are?the?same.
          ?//?Notice?the?use?of?the?wildcard.
          ?boolean?sameAvg(StatsWildCard<?>?ob)?{
          ??if?(average()?==?ob.average())
          ???return?true;
          ??return?false;
          ?}
          }

          public?class?GenericsExampleByWildcard?{

          ?/**
          ??*?@param?args
          ??*/
          ?public?static?void?main(String[]?args)?{
          ??//?TODO?Auto-generated?method?stub
          ??Integer?inums[]?=?{?1,?2,?3,?4,?5?};
          ??StatsWildCard<Integer>?iob?=?new?StatsWildCard<Integer>(inums);
          ??double?v?=?iob.average();
          ??System.out.println("iob?average?is?"?+?v);
          ??Double?dnums[]?=?{?1.1,?2.2,?3.3,?4.4,?5.5?};
          ??StatsWildCard<Double>?dob?=?new?StatsWildCard<Double>(dnums);
          ??double?w?=?dob.average();
          ??System.out.println("dob?average?is?"?+?w);
          ??Float?fnums[]?=?{?1.0F,?2.0F,?3.0F,?4.0F,?5.0F?};
          ??StatsWildCard<Float>?fob?=?new?StatsWildCard<Float>(fnums);
          ??double?x?=?fob.average();
          ??System.out.println("fob?average?is?"?+?x);
          ??//?See?which?arrays?have?same?average.
          ??System.out.print("Averages?of?iob?and?dob?");
          ??if?(iob.sameAvg(dob))
          ???System.out.println("are?the?same.");
          ??else
          ???System.out.println("differ.");
          ??System.out.print("Averages?of?iob?and?fob?");
          ??if?(iob.sameAvg(fob))
          ???System.out.println("are?the?same.");
          ??else
          ???System.out.println("differ.");

          ?}

          }


          5.使用邊界通配符
          //Example?15
          class?TwoD?{?
          ??int?x,?y;?
          ??TwoD(int?a,?int?b)?{?
          ????x?=?a;?
          ????y?=?b;?
          ??}?
          }?
          //?Three-dimensional?coordinates.?
          class?ThreeD?extends?TwoD?{?
          ??int?z;?
          ??ThreeD(int?a,?int?b,?int?c)?{?
          ????super(a,?b);?
          ????z?=?c;?
          ??}?
          }?
          //?Four-dimensional?coordinates.?
          class?FourD?extends?ThreeD?{?
          ??int?t;?
          ??FourD(int?a,?int?b,?int?c,?int?d)?{?
          ????super(a,?b,?c);?
          ????t?=?d;??
          ??}?
          }?
          //?This?class?holds?an?array?of?coordinate?objects.?
          class?Coords<T?extends?TwoD>?{?
          ??T[]?coords;?
          ??Coords(T[]?o)?{?coords?=?o;?}?
          }?
          //?Demonstrate?a?bounded?wildcard.?
          public?class?BoundedWildcard?{?
          ??static?void?showXY(Coords<?>?c)?{?
          ????System.out.println("X?Y?Coordinates:");?
          ????for(int?i=0;?i?<?c.coords.length;?i++)?
          ??????System.out.println(c.coords[i].x?+?"?"?+?
          ?????????????????????????c.coords[i].y);?
          ????System.out.println();?
          ??}?
          ??static?void?showXYZ(Coords<??extends?ThreeD>?c)?{?
          ????System.out.println("X?Y?Z?Coordinates:");?
          ????for(int?i=0;?i?<?c.coords.length;?i++)?
          ??????System.out.println(c.coords[i].x?+?"?"?+?
          ?????????????????????????c.coords[i].y?+?"?"?+?
          ?????????????????????????c.coords[i].z);?
          ????System.out.println();?
          ??}?
          ??static?void?showAll(Coords<??extends?FourD>?c)?{?
          ????System.out.println("X?Y?Z?T?Coordinates:");?
          ????for(int?i=0;?i?<?c.coords.length;?i++)?
          ??????System.out.println(c.coords[i].x?+?"?"?+?
          ?????????????????????????c.coords[i].y?+?"?"?+?
          ?????????????????????????c.coords[i].z?+?"?"?+?
          ?????????????????????????c.coords[i].t);?
          ????System.out.println();?
          ??}?
          ??public?static?void?main(String?args[])?{?
          ????TwoD?td[]?=?{?
          ??????new?TwoD(0,?0),?
          ??????new?TwoD(7,?9),?
          ??????new?TwoD(18,?4),?
          ??????new?TwoD(-1,?-23)?
          ????};?
          ????Coords<TwoD>?tdlocs?=?new?Coords<TwoD>(td);?????
          ????System.out.println("Contents?of?tdlocs.");?
          ????showXY(tdlocs);?//?OK,?is?a?TwoD?
          //??showXYZ(tdlocs);?//?Error,?not?a?ThreeD?
          //??showAll(tdlocs);?//?Erorr,?not?a?FourD?
          ????//?Now,?create?some?FourD?objects.?
          ????FourD?fd[]?=?{?
          ??????new?FourD(1,?2,?3,?4),?
          ??????new?FourD(6,?8,?14,?8),?
          ??????new?FourD(22,?9,?4,?9),?
          ??????new?FourD(3,?-2,?-23,?17)?
          ????};?
          ????Coords<FourD>?fdlocs?=?new?Coords<FourD>(fd);?????
          ????System.out.println("Contents?of?fdlocs.");?
          ????//?These?are?all?OK.?
          ????showXY(fdlocs);??
          ????showXYZ(fdlocs);?
          ????showAll(fdlocs);?
          ??}?
          }?



          6.ArrayList的Generics
          //Example?16
          public?class?ArrayListGenericDemo?{
          ??public?static?void?main(String[]?args)?{
          ????ArrayList<String>?data?=?new?ArrayList<String>();
          ????data.add("hello");
          ????data.add("goodbye");

          ????//?data.add(new?Date());?This?won't?compile!

          ????Iterator<String>?it?=?data.iterator();
          ????while?(it.hasNext())?{
          ??????String?s?=?it.next();
          ??????System.out.println(s);
          ????}
          ??}
          }?


          7.HashMap的Generics
          //Example?17
          public?class?HashDemoGeneric?{
          ??public?static?void?main(String[]?args)?{
          ????HashMap<Integer,String>?map?=?new?HashMap<Integer,String>();

          ????map.put(1,?"Ian");
          ????map.put(42,?"Scott");
          ????map.put(123,?"Somebody?else");

          ????String?name?=?map.get(42);
          ????System.out.println(name);
          ??}
          }?


          8.接口的Generics
          //Example?18
          interface?MinMax<T?extends?Comparable<T>>?{?
          ??T?min();?
          ??T?max();?
          }?
          //?Now,?implement?MinMax?
          class?MyClass<T?extends?Comparable<T>>?implements?MinMax<T>?{?
          ??T[]?vals;?
          ??MyClass(T[]?o)?{?vals?=?o;?}?
          ??//?Return?the?minimum?value?in?vals.?
          ??public?T?min()?{?
          ????T?v?=?vals[0];?
          ????for(int?i=1;?i?<?vals.length;?i++)?
          ??????if(vals[i].compareTo(v)?<?0)?v?=?vals[i];?
          ????return?v;?
          ??}?
          ??//?Return?the?maximum?value?in?vals.?
          ??public?T?max()?{?
          ????T?v?=?vals[0];?
          ????for(int?i=1;?i?<?vals.length;?i++)?
          ??????if(vals[i].compareTo(v)?>?0)?v?=?vals[i];?
          ????return?v;?
          ??}?
          }?
          public?class?GenIFDemo?{?
          ??public?static?void?main(String?args[])?{?
          ????Integer?inums[]?=?{3,?6,?2,?8,?6?};?
          ????Character?chs[]?=?{'b',?'r',?'p',?'w'?};?
          ????MyClass<Integer>?iob?=?new?MyClass<Integer>(inums);?
          ????MyClass<Character>?cob?=?new?MyClass<Character>(chs);?
          ????System.out.println("Max?value?in?inums:?"?+?iob.max());?
          ????System.out.println("Min?value?in?inums:?"?+?iob.min());?
          ????System.out.println("Max?value?in?chs:?"?+?cob.max());?
          ????System.out.println("Min?value?in?chs:?"?+?cob.min());?
          ??}?
          }


          9.Exception的Generics
          //Example?20
          interface?Executor<E?extends?Exception>?{
          ????void?execute()?throws?E;
          }

          public?class?GenericExceptionTest?{
          ????public?static?void?main(String?args[])?{
          ????????try?{
          ????????????Executor<IOException>?e?=
          ????????????????new?Executor<IOException>()?{
          ????????????????public?void?execute()?throws?IOException
          ????????????????{
          ????????????????????//?code?here?that?may?throw?an
          ????????????????????//?IOException?or?a?subtype?of
          ????????????????????//?IOException
          ????????????????}
          ????????????};

          ????????????e.execute();
          ????????}?catch(IOException?ioe)?{
          ????????????System.out.println("IOException:?"?+?ioe);
          ????????????ioe.printStackTrace();
          ????????}
          ????}
          }??
          表現(xiàn)層框架Struts/Tapestry/JSF比較
          xuyy_cn 轉(zhuǎn)貼???更新:2006-02-27 18:55:13??版本: 1.0 ??

          ????Struts/Tapestry/JSF是目前J2EE表現(xiàn)層新老組合的框架技術(shù)。從誕生時(shí)間上看,Struts應(yīng)該比較早,使用得非常廣泛,Tapestry??3.0逐漸引起廣泛的重視,正當(dāng)Tapestry即將大顯身手時(shí)期,SUN推出JSF標(biāo)準(zhǔn)技術(shù),雖然JSF一開始推出尚不成熟,留出了一段空白期,但是隨著JSF1.1標(biāo)準(zhǔn)推出,JSF開始正面出擊,粉面隆重登場了。??
            其實(shí),JSF和Tapestry也并不是那種頭碰頭的相同競爭性技術(shù),兩者還是各有側(cè)重點(diǎn)的,不過比較細(xì)微,但是這種細(xì)微點(diǎn)在實(shí)現(xiàn)一個大工程時(shí)可能帶來不同的感受和變化。

            首先,我們從一個高度來抽象一下表現(xiàn)層框架應(yīng)有的技術(shù)架構(gòu),下圖可以說所有表現(xiàn)層框架技術(shù)都必須實(shí)現(xiàn)的功能架構(gòu)圖:



            當(dāng)然,我們不必廢話羅嗦MVC模式,MVC模式是基準(zhǔn)模式,現(xiàn)在框架技術(shù)已經(jīng)不必再拼是否是MVC模式了。??在上圖MVC模式基礎(chǔ)上,一個表現(xiàn)層框架無外乎要實(shí)現(xiàn)圖中的三個功能:

            1.在當(dāng)前頁面能夠顯示一個組件對象的內(nèi)容;而不是象純JSP那樣,需要在Jsp頁面寫入“調(diào)用對象方法”的Java代碼。

            2.當(dāng)用戶按下頁面的提交按扭或鏈接后,事件發(fā)生,這時(shí)應(yīng)該觸發(fā)服務(wù)器端并將當(dāng)前頁面的參數(shù)提交給服務(wù)器。這種機(jī)制表現(xiàn)在Form表單提交和有參數(shù)的鏈接<a??href=""></a>

            3.從一個頁面視圖直接跳轉(zhuǎn)到另外一個頁面視圖,單純的導(dǎo)航作用。

            我們通過下表來比較這??三種框架在實(shí)現(xiàn)上圖各個功能時(shí)技術(shù)細(xì)節(jié),從而得出他們的異同點(diǎn)和偏重點(diǎn)。

          ????Struts??Tapestry3.0??JSF??
          在View顯示的組件要求??組件必須繼承ActionForm
          ??分顯式調(diào)用和隱式調(diào)用
          組件必須繼承BaseComponent??普通POJO
          無需繼承
          Managed??Bean??
          組件在View顯示粒度??View頁面只能顯示與表單對應(yīng)的ActionForm,配置中Action??ActionForm??頁面一般只能1:1:1關(guān)系。??可將組件嵌入頁面任何一行,對使用組件數(shù)量無限制。??同Tapestry??
          頁面分區(qū)tiles??使用Tiles標(biāo)簽庫實(shí)現(xiàn),需要另外tiles-def.xml配置文件??組件有自己的視圖頁面,通過調(diào)用組件即直接實(shí)現(xiàn)多個頁面組合。強(qiáng)大自然的頁面組合是其特點(diǎn)。??通過組件+標(biāo)簽庫實(shí)現(xiàn)Subview,但如需重用Layout,還要結(jié)合Tiles.??
          頁面跳轉(zhuǎn)??使用標(biāo)簽庫html:link中寫明目標(biāo)URL,URL名稱需要對照配置文件的path命名,與組件Action耦合。??URL名稱是目標(biāo)的組件名稱,不涉及URL和路徑等操作,方便穩(wěn)固。??類似Struts,也需要在配置文件中查找,與組件分離。??
          參數(shù)傳遞??使用html:link時(shí)傳遞參數(shù)超過一個以上處理麻煩。??直接調(diào)用組件,直接賦予參數(shù),沒有參數(shù)個數(shù)限制??參數(shù)分離傳遞給組件??
          事件觸發(fā)??通過表單提交submit激活,不能細(xì)化到表單里字段。??能夠給于表單每個字段貼一個事件,事件組件必須實(shí)現(xiàn)PageListener接口??同Tapestry,事件組件必須實(shí)習(xí)ActionListener??接口??
          ??

            Struts組件編程模型

            Struts實(shí)現(xiàn)組件編程時(shí)有一些復(fù)雜:經(jīng)常為一個頁面中需要引入多個組件而頭疼,因?yàn)镾truts中無法直接引入多個組件,必須繞一些圈子:

            一般分兩種情況:如果同一個Action就可以對付這些組件,那么在這種情況下有兩個辦法:

            1.將這多個組件裝入一個ActionForm中,如使用MapForm等機(jī)制;

            2.手工將多個組件裝入request/session等scope中,然后根據(jù)其名稱在jsp中獲得。

            這兩個方法都有缺點(diǎn):??第一種辦法經(jīng)常一個ActionForm弄得面目全非,變成一個大雜燴,違反了OO分派封裝的原則;第2種辦法其實(shí)又回到j(luò)sp編程;

            第二種情況,如果這些組件必須有預(yù)先由不同的Action來處理,每個組件必須經(jīng)過Action??-->ActionForm流程,在這種情況下有兩種辦法:

            1.使用Tiles,??不同流程輸出到同一個頁面的不同區(qū)域。是一種并行處理方式。

            2.??對多個流程首尾相連,第一Action??forward結(jié)果是第二個Action,最后輸出一個Jsp,在這個jsp中就可以使用前面多個流程的多個ActionForm了,這屬于串行方式。

            Struts組件模型缺點(diǎn)

            Struts組件編程必須限定在Action/ActionForm/JSP這三個框框中做文章,難度相對比較大,而Tapestry/JSF則沒有太多這些技術(shù)框框限制,兩者在組件編程方面更讓編程者自由一些,方便一些,這也是組件型框架的優(yōu)勢吧。

            Struts標(biāo)簽庫

            在Struts中,經(jīng)常需要使用標(biāo)簽庫來顯示組件ActionForm中內(nèi)容,這就涉及到一個結(jié)合的問題,標(biāo)簽庫是別人寫的,參考Struts的標(biāo)簽庫用法,而組件是自己的,難度和麻煩就體現(xiàn)在這個結(jié)合點(diǎn)上。

            JSF基本思路和Struts差不多,只不過換了不同標(biāo)簽庫,也需要標(biāo)簽庫+組件的結(jié)合思考,不過因?yàn)榻M件這里是通用組件,沒有什么限制,所以這樣比Struts要輕松一些。

            Tapestry使用了組件庫概念替代了標(biāo)簽庫,沒有標(biāo)簽庫概念,這樣就沒有標(biāo)簽庫和自己的組件需要結(jié)合的問題,都是組件的使用,組件中分Tapestry標(biāo)準(zhǔn)組件和自己定義的組件,這也是接觸了Jsp體系的人學(xué)習(xí)Tapestry面臨的一個思路轉(zhuǎn)換。

            具體以頁面跳轉(zhuǎn)為例子,頁面跳轉(zhuǎn)是靠鏈接<a??href="目標(biāo)"></a>??實(shí)現(xiàn),鏈接是頁面經(jīng)常使用的元素。

            Struts提供的html:link在頻繁使用就特別不方便,尤其在傳遞多個參數(shù)時(shí):其中html:link的page值,是跳轉(zhuǎn)對方頁面或Action的path,這個path一般需要到struts-config.xml查找Action的相應(yīng)path,一旦配置文件path值修改,涉及到這個所有相關(guān)頁面都要修改。

            JSF將鏈接概念劃分兩個方面:導(dǎo)航性質(zhì)和事件激活,在導(dǎo)航方面還是需要到配置faces-config查詢Navigation的from-outcome的值。

            由于Tapestry沒有標(biāo)簽庫概念,只有組件或頁面兩個概念,因此,鏈接跳轉(zhuǎn)目標(biāo)要么是組件,要么是頁面,簡潔簡單,它沒有多余的path概念,就是組件名,也就是對象名稱,組件名稱和path名稱合二為一。

            總結(jié)

            JSF在很大程度上類似Struts,而不是類似Tapestry,可以說是一種Struts??2.0,都是采取標(biāo)簽庫+組件的形式,只是JSF的組件概念沒有象Struts那樣必須繼承ActionForm的限制;JSF在事件粒度上要細(xì)膩,不象Struts那樣,一個表單一個事件,JSF可以細(xì)化到表單中的每個字段上。

            JSF只有在組件和事件機(jī)制這個概念上類似Tapestry,但是不似Tapestry那樣是一個完全組件的框架,所以,如果你做一個對頁面要求靈活度相當(dāng)高的系統(tǒng),選用Tapestry是第一考慮。

            Struts/JSF則適合在一般的數(shù)據(jù)頁面錄入的系統(tǒng)中,對于Struts和JSF的選用,我目前個人觀點(diǎn)是:如果你是一個新的系統(tǒng),可以直接從JSF開始;如果你已經(jīng)使用Struts,不必轉(zhuǎn)換,如果需要切換,可以將JSF和Tapestry一起考慮。

            另外,JSF/Tapestry不只是支持Html,也支持多種客戶端語言如WML或XUI等。

            這三者之間關(guān)系:如果說Struts是左派;那Tapestry則是右派;而JSF則是中間派,中庸主義是SUN聯(lián)盟的一貫策略。

            當(dāng)然,你也可以發(fā)表你在實(shí)踐中這三者任何一個的使用感受,以使得后來者有一個比較。? ?
          當(dāng)前流行的J2EE?WEB應(yīng)用架構(gòu)分析
          xuyy_cn 原創(chuàng)???更新:2006-09-14 13:04:12??版本: 1.0 ??

          1.?架構(gòu)概述?


          J2EE體系包括java?server?pages(JSP)?,java?SERVLET,?enterprise?bean,WEB?service等技術(shù)。這些技術(shù)的出現(xiàn)給電子商務(wù)時(shí)代的WEB應(yīng)用程序的開發(fā)提供了一個非常有競爭力的選擇。怎樣把這些技術(shù)組合起來形成一個適應(yīng)項(xiàng)目需要的穩(wěn)定架構(gòu)是項(xiàng)目開發(fā)過程中一個非常重要的步驟。完成這個步驟可以形成一個主要里程碑基線。形成這個基線有很多好處:?


          各種因數(shù)初步確定?

          為了形成架構(gòu)基線,架構(gòu)設(shè)計(jì)師要對平臺(體系)中的技術(shù)進(jìn)行篩選,各種利弊的權(quán)衡。往往架構(gòu)設(shè)計(jì)師在這個過程中要閱讀大量的技術(shù)資料,聽取項(xiàng)目組成員的建議,考慮領(lǐng)域?qū)<业男枨螅紤]贊助商成本(包括開發(fā)成本和運(yùn)行維護(hù)成本)限額。一旦架構(gòu)設(shè)計(jì)經(jīng)過評審,這些因數(shù)初步地就有了在整個項(xiàng)目過程中的對項(xiàng)目起多大作用的定位。?

          定向技術(shù)培訓(xùn)?

          一旦架構(gòu)師設(shè)計(jì)的架構(gòu)得到了批準(zhǔn)形成了基線,項(xiàng)目開發(fā)和運(yùn)行所采用的技術(shù)基本確定下來了。眾多的項(xiàng)目經(jīng)理都會對預(yù)備項(xiàng)目組成員的技術(shù)功底感到擔(dān)心;他們需要培訓(xùn)部門提供培訓(xùn),但就架構(gòu)師面對的技術(shù)海洋,項(xiàng)目經(jīng)理根本就提不出明確的技術(shù)培訓(xùn)需求。怎不能夠?qū)w系中所有技術(shù)都進(jìn)行培訓(xùn)吧!有了架構(gòu)里程碑基線,項(xiàng)目經(jīng)理能確定這個項(xiàng)目開發(fā)會采用什么技術(shù),這是提出培訓(xùn)需求應(yīng)該是最精確的。不過在實(shí)際項(xiàng)目開發(fā)中,技術(shù)培訓(xùn)可以在基線確定之前與架構(gòu)設(shè)計(jì)并發(fā)進(jìn)行。?

          角色分工?

          有了一個好的架構(gòu)藍(lán)圖,我們就能準(zhǔn)確劃分工作。如網(wǎng)頁設(shè)計(jì),JSP?標(biāo)簽處理類設(shè)計(jì),SERVLET?設(shè)計(jì),session?bean設(shè)計(jì),還有各種實(shí)現(xiàn)。這些任務(wù)在架構(gòu)藍(lán)圖上都可以清晰地標(biāo)出位置,使得項(xiàng)目組成員能很好地定位自己的任務(wù)。一個好的架構(gòu)藍(lán)圖同時(shí)也能規(guī)范化任務(wù),能很好地把任務(wù)劃分為幾類,在同一類中的任務(wù)的工作量和性質(zhì)相同或相似。這樣工作量估計(jì)起來有一個非常好的基礎(chǔ)。?

          運(yùn)行維護(hù)?

          前面說過各個任務(wù)在架構(gòu)圖上都有比較好的定位。任何人能借助它很快地熟悉整個項(xiàng)目的運(yùn)行情況,錯誤出現(xiàn)時(shí)能比較快速地定位錯誤點(diǎn)。另外,有了清晰的架構(gòu)圖,項(xiàng)目版本管理也有很好的版本樹軀干。?

          擴(kuò)展性?

          架構(gòu)猶如一顆參天大樹的軀干,只要軀干根系牢,樹干粗,長一些旁支,加一些樹葉輕而易舉無疑。同樣,有一個穩(wěn)定的經(jīng)得起考驗(yàn)的架構(gòu),增加一兩個業(yè)務(wù)組件是非??焖俸腿菀椎?。?

          大家都知道這些好處,一心想形成一個這樣的J2EE應(yīng)用程序架構(gòu)(就像在windows平臺中的MFC)。在這個路程中經(jīng)歷了兩個大的階段:?


          1.1.?模型1?


          模型1其實(shí)不是一個什么穩(wěn)定架構(gòu),甚至談不上形成了架構(gòu)。模型1的基礎(chǔ)是JSP文件。它從HTTP的請求中提取參數(shù),調(diào)用相應(yīng)的業(yè)務(wù)邏輯,處理HTTP會話,最后生成HTTP文檔。一系列這樣的JSP文件形成一個完整的模型1應(yīng)用,當(dāng)然可能會有其他輔助類或文件。早期的ASP?和?PHP?技術(shù)就屬于這個情況。?


          總的看來,這個模型的好處是簡單,但是它把業(yè)務(wù)邏輯和表現(xiàn)混在一塊,對大應(yīng)用來說,這個缺點(diǎn)是令人容忍不了的。?


          1.2.?模型2?


          在經(jīng)過一番實(shí)踐,并廣泛借鑒和總結(jié)經(jīng)驗(yàn)教訓(xùn)之后,J2EE應(yīng)用程序終于迎來了MVC(模型-視圖-控制)模式。MVC模式并不是J2EE行業(yè)人士標(biāo)新立異的,所以前面我談到廣發(fā)借鑒。MVC的核心就是做到三層甚至多層的松散耦合。這對基于組件的,所覆蓋的技術(shù)不斷膨脹的J2EE體系來說真是福音和救星。?


          它在瀏覽器(本文對客戶代理都稱瀏覽器)和JSP或SERVLET之間插入一個控制組件。這個控制組件集中了處理瀏覽器發(fā)過來的HTTP請求的分發(fā)邏輯,也就是說,它會根據(jù)HTTP請求的URL,輸入?yún)?shù),和目前應(yīng)用的內(nèi)部狀態(tài),把請求分發(fā)給相應(yīng)的WEB?層的JSP?或SERVLET。另外它也負(fù)責(zé)選擇下一個視圖(在J2EE中,JSP,SERVLET會生成回給瀏覽器的html從而形成視圖)。集中的控制組件也有利于安全驗(yàn)證,日志紀(jì)錄,有時(shí)也封裝請求數(shù)據(jù)給下面的WEB?tier層。這一套邏輯的實(shí)現(xiàn)形成了一個像MFC的應(yīng)用框架,位置如圖:?


          1.3.?多層應(yīng)用?


          下圖為J2EE體系中典型的多層應(yīng)用模型。?


          Client?tier客戶層?

          一般為瀏覽器或其他應(yīng)用。客戶層普遍地支持HTTP協(xié)議,也稱客戶代理。?

          WEB?tier?WEB應(yīng)用層?

          在J2EE中,這一層由WEB?容器運(yùn)行,它包括JSP,?SERVLET等WEB部件。?

          EJB?tier?企業(yè)組件層?

          企業(yè)組件層由EJB容器運(yùn)行,支持EJB,?JMS,?JTA?等服務(wù)和技術(shù)。?

          EIS?tier?企業(yè)信息系統(tǒng)層?

          企業(yè)信息系統(tǒng)包含企業(yè)內(nèi)傳統(tǒng)信息系統(tǒng)如財(cái)務(wù),CRM等,特點(diǎn)是有數(shù)據(jù)庫系統(tǒng)的支持。?


          應(yīng)用框架目前主要集中在WEB層,旨在規(guī)范這一層軟件的開發(fā)。其實(shí)企業(yè)組件層也可以實(shí)現(xiàn)這個模型,但目前主要以設(shè)計(jì)模式的形式存在。而且有些框架可以擴(kuò)充,有了企業(yè)組件層組件的參與,框架會顯得更緊湊,更自然,效率會更高。?


          2.?候選方案?


          目前,實(shí)現(xiàn)模型2的框架也在不斷的涌現(xiàn),下面列出比較有名的框架。?


          2.1.?Apache?Struts?


          Struts是一個免費(fèi)的開源的WEB層的應(yīng)用框架,apache軟件基金致力于struts的開發(fā)。Struts具是高可配置的性,和有一個不斷增長的特性列表。一個前端控制組件,一系列動作類,動作映射,處理XML的實(shí)用工具類,服務(wù)器端java?bean?的自動填充,支持驗(yàn)證的WEB?表單,國際化支持,生成HTML,實(shí)現(xiàn)表現(xiàn)邏輯和模版組成了struts的靈魂。?


          2.1.1.?Struts和MVC?


          模型2的目的和MVC的目的是一樣的,所以模型2基本可以和MVC等同起來。下圖體現(xiàn)了Struts的運(yùn)作機(jī)理:?


          2.1.1.1.?控制?


          如圖所示,它的主要部件是一個通用的控制組件。這個控制組件提供了處理所有發(fā)送到Struts?的HTTP請求的入口點(diǎn)。它截取和分發(fā)這些請求到相應(yīng)的動作類(這些動作類都是Action類的子類)。另外控制組件也負(fù)責(zé)用相應(yīng)的請求參數(shù)填充?From?bean,并傳給動作類。動作類實(shí)現(xiàn)核心商業(yè)邏輯,它可以通過訪問java?bean?或調(diào)用EJB。最后動作類把控制權(quán)傳給后續(xù)的JSP?文件,后者生成視圖。所有這些控制邏輯利用一個叫struts-config.xml文件來配置。?


          2.1.1.2.?模型?


          模型以一個或幾個java?bean的形式存在。這些bean分為三種:?


          Form?beans(表單Beans)?

          它保存了HTTP?post請求傳來的數(shù)據(jù),在Struts里,所有的Form?beans都是?ActionFrom?類的子類。?

          業(yè)務(wù)邏輯beans?

          專門用來處理業(yè)務(wù)邏輯。?

          系統(tǒng)狀態(tài)beans?

          它保存了跨越多個HTTP?請求的單個客戶的會話信息,還有系統(tǒng)狀態(tài)。?

          2.1.1.3.?視圖?


          控制組件續(xù)傳HTTP請求給實(shí)現(xiàn)了視圖的JSP文件。JSP能訪問beans?并生成結(jié)果文檔反饋到客戶。Struts提供JSP?標(biāo)簽庫:?Html,Bean,Logic,Template等來達(dá)到這個目的,并有利于分開表現(xiàn)邏輯和程序邏輯。?


          2.1.2.?Struts的細(xì)節(jié)分析?


          2.1.2.1.?視圖-控制-模型?


          用戶發(fā)出一個*.do的HTTP請求,控制組件接收到這個請求后,查找針對這個請求的動作映射,再檢查是否曾創(chuàng)建過相應(yīng)的動作對象(action實(shí)例),如果沒有則調(diào)用actionmapping生成一個動作對象,控制組件會保存這個動作對象供以后使用。接著調(diào)用actionmapping的方法得到actionForm對象。之后把a(bǔ)ctionForm作為參數(shù)傳給動作對象的perform方法,這個方法結(jié)束之后會返回給控制組件一個?actionforward對象??刂平M件接著從這個對象中獲取下一個視圖的路徑和重定向?qū)傩?。如果為重定向則調(diào)用HTTPSERVLETREPONSE的方法來顯示下一個視圖,否則相繼調(diào)用requestdispatcher,?SERVLETcontext的方法續(xù)傳HTTP請求到下一個視圖。?


          當(dāng)動作對象運(yùn)行perform方法時(shí),可能出現(xiàn)錯誤信息。動作對象可以保存這些錯誤信息到一個error對象中,接著調(diào)用自身的saveerrors方法把這個錯誤保存到request對象的屬性中。接著動作對象調(diào)用actionmapping對象的getInput方法從動作映射中獲取input參數(shù),也就是產(chǎn)生輸入的視圖,并以這個input為參數(shù)生成一個actionforward對象返回。這個input參數(shù)的JSP中一般有HTTP:errors定制標(biāo)簽讀取這些錯誤信息并顯示在頁面上。?


          2.1.2.2.?模型到視圖?


          模型到視圖指視圖在顯示之前裝載系統(tǒng)數(shù)據(jù)到視圖的過程。系統(tǒng)數(shù)據(jù)一般為模型內(nèi)java?bean的信息。示意圖表現(xiàn)了由控制組件forward過來的有html:form定制標(biāo)簽的JSP?的處理邏輯。?


          html:form定制標(biāo)簽處理對象從application?scope(通過查詢SERVLETCONTEXT對象的屬性來實(shí)現(xiàn))獲取先前由控制組件actionSERVLET放在那里的動作映射等對象,由html:form?的action屬性查得actionform名字、類型和范圍等信息,在相應(yīng)的范圍內(nèi)查找actionform,如果有則利用它的信息填充html?form表單[實(shí)際填充動作在嵌套的html:text等定制標(biāo)簽的處理對象中]。否則在相應(yīng)范圍內(nèi)創(chuàng)建一個actionform?對象。?


          2.1.3.?優(yōu)缺點(diǎn)?


          優(yōu)點(diǎn):?


          一些開發(fā)商開始采用并推廣這個框架?

          作為開源項(xiàng)目,有很多先進(jìn)的實(shí)現(xiàn)思想?

          對大型的應(yīng)用支持的較好?

          有集中的網(wǎng)頁導(dǎo)航定義?

          缺點(diǎn):?


          不是業(yè)屆標(biāo)準(zhǔn)?

          對開發(fā)工具的支持不夠?

          復(fù)雜的taglib,需要比較長的時(shí)間來掌握?

          html?form?和?actionform的搭配比較封閉,但這也是它的精華所在。?

          修改建議?

          把a(bǔ)ctionform屬性的設(shè)置器和訪問器修改成讀取或生成xml文檔的方法,然后?html?form和actionform之間用xml文檔進(jìn)行數(shù)據(jù)交換,使之松散耦合,適應(yīng)數(shù)據(jù)結(jié)構(gòu)易變化的應(yīng)用。?


          2.2.?JATO?


          JATO應(yīng)用程序框架是iPlanet?應(yīng)用程序框架的舊名。它是一個成熟的、強(qiáng)大的,基于J2EE標(biāo)準(zhǔn)的面向于開發(fā)WEB應(yīng)用程序的應(yīng)用框架。結(jié)合了顯示字段、應(yīng)用程序事件、組件層次和以頁面為中心的開發(fā)方法、以及MVC和服務(wù)到工作者service-to-workers的設(shè)計(jì)模式等概念。JATO可適用于中、大、超大規(guī)模的WEB應(yīng)用。但是它也不是一個企業(yè)層的應(yīng)用框架,也就是說它不會直接提供創(chuàng)建EJB,?WEB?services等企業(yè)層組件的方法,但用它可以構(gòu)造出訪問企業(yè)層組件的客戶應(yīng)用。?


          這個框架功能主要有三部分組成:?


          iPlanet應(yīng)用框架核心;?

          iPlanet應(yīng)用框架組件;?

          iPlanet應(yīng)用框架擴(kuò)展。?

          應(yīng)用框架核心定義了基本接口、對象協(xié)議、簡單組件,以及iPlanet應(yīng)用框架程序的最小核心。包括視圖簡單組件、模型簡單組件、請求分發(fā)組件和可重用命令對象。iPlanet應(yīng)用框架組件利用框架核心定義的基本接口、協(xié)議和組件向開發(fā)者提供高層的重用組件,這些組件既有與特定視覺效果無關(guān)的水平組件,同時(shí)也有適應(yīng)特定實(shí)用環(huán)境、提高可用性而特意提供的垂直型組件??蚣軘U(kuò)展實(shí)現(xiàn)了用框架相容的方法訪問非J2EE環(huán)境的方法。通常情況下,擴(kuò)展被框架應(yīng)用程序用來無縫訪問J2EE容器特定功能。JATO平臺棧圖很清楚地表達(dá)了這個情況。?


          JATO最大的威力在:對于快速開發(fā)用戶,你能利用框架組件和擴(kuò)展提高生產(chǎn)率,對于要求更大靈活性的用戶,你能實(shí)現(xiàn)框架核心提供的接口來保持應(yīng)用的框架兼容性。?

          此圖表示實(shí)現(xiàn)一個JATO應(yīng)用程序,可以簡單地實(shí)現(xiàn)控制組件module1Servlet,視圖組件ListCustomersViewBean和模型組件CustomersModuleImpl,以及一個給客戶代理顯示界面的ListCustomers.jsp文件。并清楚地表明這些組件與JATO框架組件的繼承關(guān)系。?


          JATO標(biāo)簽庫提供了VIEW對象與JSP文件的接口。庫中標(biāo)簽處理程序負(fù)責(zé)實(shí)現(xiàn)VIEW對象和JSP產(chǎn)生地客戶端文檔的信息同步和交換。這個圖清楚地表達(dá)了這種對應(yīng)關(guān)系?


          2.2.1.?MVC分析?


          前端控制組件接收用戶發(fā)來的任何請求,這個可在WEB.xml中指定請求分發(fā)組件負(fù)責(zé)視圖管理和導(dǎo)航,和前端控制組件封裝在ApplicationSERVLETBase一起實(shí)現(xiàn)。應(yīng)用程序開發(fā)者需要為每一個子系統(tǒng)(人力資源,財(cái)務(wù),CRM等)實(shí)現(xiàn)一個此類的繼承。?


          請求分發(fā)組件分發(fā)請求給工作者,工作者實(shí)現(xiàn)了command接口。應(yīng)用開發(fā)者可以實(shí)現(xiàn)這個接口。JATO提供了一個缺省實(shí)現(xiàn):DefaultRequestHandingCommand,這個實(shí)現(xiàn)會把請求傳給視圖組件的特定事件。?


          組合視圖是指視圖組件在顯示給用戶時(shí)的層次關(guān)系:根視圖是一個ViewBean類的對象字段是一個DisplayField類的對象,容器視圖是一個ContainerView類的對象。視圖組件類的層次關(guān)系如下圖:?


          2.2.2.?優(yōu)缺點(diǎn)分析?


          優(yōu)點(diǎn):?


          這種框架的適應(yīng)范圍大,即提供了底層接口,也有立即可用的組件?

          具有與客戶端RAD開發(fā)工具相似的開發(fā)概念如頁為中心(等同于VB的FORM),事件處理等.?

          對大型的應(yīng)用支持較好?

          缺點(diǎn):?


          不是業(yè)屆標(biāo)準(zhǔn)?

          目前還沒有開發(fā)工具的支持(然JATO已經(jīng)為工具支持做好了準(zhǔn)備)?

          沒有定義網(wǎng)頁導(dǎo)航,開發(fā)者在視圖中自己指定具體的導(dǎo)航URL?

          修改建議?

          把眾多的VIEW/MODEL對應(yīng)修改成xml文檔傳遞數(shù)據(jù),加上集中的網(wǎng)頁導(dǎo)航定義?


          2.3.?JSF(JavaServer?Faces)?


          JSF是一個包括SUN在內(nèi)的專家組正在定義的開發(fā)WEB應(yīng)用用戶界面的框架,JSF?技術(shù)包括:?


          一組API,它實(shí)現(xiàn)UI了組件,管理組件的狀態(tài),處理事件,輸入校驗(yàn),定義頁面導(dǎo)航,支持國際化和訪問;?

          一個JSP定制標(biāo)簽庫實(shí)現(xiàn)與JSP的接口。?

          JSF非常簡單,是一個定義良好的編程模型。利用這個技術(shù),開發(fā)者通過在頁面內(nèi)組合可重用的UI組件,在把這些組件和應(yīng)用的數(shù)據(jù)源相連,路由客戶產(chǎn)生的事件到服務(wù)器端的事件處理器進(jìn)行編程。JSP處理了所有幕后的復(fù)雜工作,使得開發(fā)者把關(guān)注重點(diǎn)放在應(yīng)用代碼上。?


          2.3.1.?STRUTS、JATO和JSF比較?


          它們之間有部分重疊,但重點(diǎn)不一樣。?


          STRUTS和JATO都提供了一個MVC式的應(yīng)用模型,而JSF只在用戶界面上提供編程接口。這意味著前兩者涉及的范圍比后者廣。JSF可以成為前兩者在UI開發(fā)的部分。?

          JSF的規(guī)范的發(fā)布版將在?2002年底發(fā)布,實(shí)現(xiàn)可能要比這個時(shí)間晚些。另外將會有工具支持這個框架的應(yīng)用開發(fā)。?

          2.4.?WAF?


          WAF是WEB?APPLICATION?FRAMWORK的簡稱,是SUN藍(lán)皮書例子程序中提出的應(yīng)用框架。它實(shí)現(xiàn)了?MVC和其他良好的設(shè)計(jì)模式。?


          2.4.1.?細(xì)節(jié)分析?



          2.4.2.?視圖-控制-模型?


          如圖所示,開發(fā)人員編寫的兩個xml配置文件定義了WAF的運(yùn)作參數(shù)。Screendefinition.xml定義了一系列的屏幕(screen)。Mapping.xml則定義了某個動作之后應(yīng)該顯示的屏幕,但沒有指定屏幕到哪里拿數(shù)據(jù)。?


          用戶發(fā)出一個HTTP請求(*.screen),由TemplateSERVLET屏幕前端控制組件接收,它提取請求信息,設(shè)置request對象CurrentScreen屬性,再把請求發(fā)到模版JSP。模版JSP收到請求后,JSP中的Template標(biāo)簽察看這個當(dāng)前屏幕,并從屏幕定義文件(Screendefinition.xml)中獲取這個屏幕的具體參數(shù),再生成html返回給客戶。?


          假設(shè)返回給客戶的html中包括了html表單,用戶在輸入一定數(shù)據(jù)之后提交,發(fā)出一個HTTP請求(*.do)。這個請求被MainSERVLET接收,它提取請求信息,察看動作映射文件(mapping.xml),設(shè)置處理這個請求的動作對象(HTTPAction對象),交給requestprosessor對象處理。Requestprosessor對象調(diào)用動作對象完成任務(wù),如果需要進(jìn)一步處理,requestprosessor對象會調(diào)用WEBclientcontroler對象的事件處理機(jī)制。MainSERVLET在處理完請求之后,從屏幕流管理對象那里得到下一個屏幕,并把請求傳給這個屏幕的JSP文件。?


          值得一提的是WEBclientcontroler事件處理機(jī)制最終把HTTP請求的數(shù)據(jù)傳到了EJBAction對象那里處理。這樣HTTPAction對象和EJBAction對象形成了兩級處理機(jī)制,前一級與request對象緊密相關(guān),把數(shù)據(jù)封裝起來形成一個Event對象,再傳給了EJBAction對象,后者與Request對象無關(guān)。這個方式可以形成一個session級別的數(shù)據(jù)處理機(jī)制。下圖顯示了這個方法。HTTPAction1對象處理一個請求,并把數(shù)據(jù)放到一個狀態(tài)SessionBean內(nèi),HTTPAction2也如此,當(dāng)HTTPAction3接收到HTTP請求之后,把控制傳給EJBAction,?后者獲取狀態(tài)SessionBean數(shù)據(jù),處理請求,成功后清控狀態(tài)SessionBean的內(nèi)容。這個機(jī)制非常適應(yīng)多個輸入頁面才能滿足一個業(yè)務(wù)的輸入數(shù)據(jù)的情況(比如購物車)。?


          2.4.3.?優(yōu)缺點(diǎn)分析?


          優(yōu)點(diǎn)?


          屏幕導(dǎo)航定義明確?

          為框架的擴(kuò)展提供了一個空間?

          缺點(diǎn)?


          源碼比較亂,穩(wěn)定性和可靠性沒人驗(yàn)證。?

          只是一個框架軀干,沒有正式的model層,視圖的概念不強(qiáng)?

          沒有模型到視圖的定義?

          修改意見?

          只有一個框架軀干,正為實(shí)現(xiàn)自己的應(yīng)用框架提供了靈活性。沒有僵化的視圖概念,提供了在網(wǎng)頁輸入到模型的擴(kuò)充接口,比如插入XML數(shù)據(jù)交換。?
          Eclipse?RCP?的一些有用的資源及應(yīng)用案例
          yipsilon 原創(chuàng)???更新:2006-12-08 18:39:06??版本: 1.0 ??

          初學(xué)者有用的參考網(wǎng)站:

          Eclipse官方:?http://www.eclipse.org

          中國Eclipse社區(qū):?http://www.eclipseworld.org

          IBM?DeveloperWorks?Eclipse專題:?http://www-128.ibm.com/developerworks/cn/opensource/top-projects/eclipse.html

          Planet?Eclipse:?http://www.planeteclipse.org

          Eclipse?Zone:?http://www.eclipsezone.com

          下面是一些應(yīng)用的截圖:

          點(diǎn)擊查看原圖
          地圖應(yīng)用(1)

          點(diǎn)擊查看原圖
          地圖應(yīng)用(2)

          點(diǎn)擊查看原圖
          辦公自動化

          點(diǎn)擊查看原圖
          空間任務(wù)管理

          點(diǎn)擊查看原圖
          生物化學(xué)

          點(diǎn)擊查看原圖
          證券交易

          點(diǎn)擊查看原圖
          ERP應(yīng)用

          點(diǎn)擊查看原圖
          教育應(yīng)用(課件)

          點(diǎn)擊查看原圖
          教育應(yīng)用(管理)

          點(diǎn)擊查看原圖
          個人信息管理

          點(diǎn)擊查看原圖
          數(shù)學(xué)應(yīng)用

          點(diǎn)擊查看原圖
          互聯(lián)網(wǎng)應(yīng)用(BT客戶端)

          posted on 2007-03-31 20:20 MEYE 閱讀(3925) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 大名县| 成武县| 体育| 阳泉市| 托里县| 城固县| 合山市| 丹东市| 古浪县| 东至县| 佛冈县| 深州市| 台州市| 蓬溪县| 额尔古纳市| 浦东新区| 九江市| 上饶县| 东平县| 纳雍县| 桂东县| 乐陵市| 汝南县| 报价| 化州市| 赤水市| 华安县| 辽宁省| 金阳县| 天台县| 额敏县| 澄江县| 鹤壁市| 土默特左旗| 菏泽市| 南部县| 贡嘎县| 金寨县| 佛山市| 汤阴县| 保康县|