2006年12月18日
Using Kettle for more than one year in my project, but still have no time to read the source code of Kettle untill couple of days before (busy or actually lazy?). Yeah, I am a lazy dog...
Since the source code is not available directly from the Pentato website, I had no choice but to go thru some posts on the Pentaho forum one by one. It didn't take me much effor before I found the relevant post, thx god:) The source code of Kettle now is maintained in SVN of JavaForge, and anybody "can get the latest(subversion trunk) code changes with on this URL: http://svn.javaforge.com/svn/Kettle/trunk", the username is "anonymous" and password is "anon".
Besides, I had to download a SVN tool. I am using TortoiseSVN in my company, and the ux satisfied me, so I chose it again.
It taked about 30 minutes to check out all the source code (still very fast I think, my bandwidth is 2M).
When it done, I imported it as a general project into eclipse, and one thing surprised me a little bit was that, I didn't have to compile the source code or import some jars even than execute a Ant target manually as I built the source code of Spring before. Hah, Kettle is developed by using eclipse?! (you can find some eclipse project files in the trunk).
Anyway, the work was going on smoothly, no more than 40 minutes. Now I can debug the Kettle, and from my experience, in some scenarios Kettle doesn't work in good performace than I expect, and even sometimes it runs into some bugs when I do the multitudinous insert or update operations. Here I get the opportunity to look into the code and figure out the problem.
Keep moving forward...
java
.util.*定義了一系列的接口和類,告訴我們用什么類NEW出一個(gè)對(duì)象,可以進(jìn)行超越數(shù)組的操作。
(注:JAVA1.5對(duì)JAVA1.4的最大改進(jìn)就是增加了對(duì)范型的支持)
Set接口是數(shù)學(xué)中集合的概念:其元素?zé)o序,且不可重復(fù)。(正好與List對(duì)應(yīng))
!!注意數(shù)組和集合的區(qū)別:數(shù)組中只能存簡(jiǎn)單數(shù)據(jù)類型。Collection接口和Map接口只能存對(duì)象。
只有List可用get和size。而Set則不可用(因其無(wú)序)。
集合中每一個(gè)元素都有對(duì)象,如有字符串要經(jīng)過(guò)強(qiáng)制類型轉(zhuǎn)換。
Collections是工具類,所有方法均為有用方法,且方法為static。
Collections.Sort()分為兩部分,一部分為排序規(guī)則;一部分為排序算法。
規(guī)則用來(lái)判斷對(duì)象;算法是考慮如何排序。
對(duì)于自定義對(duì)象,Sort不知道規(guī)則,所以無(wú)法比較。這種情況下一定要定義排序規(guī)則。方式有兩種:
① java.lang下面有一個(gè)接口:Comparable(可比較的)
可以讓自定義對(duì)象實(shí)現(xiàn)一個(gè)接口,這個(gè)接口只有一個(gè)方法comparableTo(Object o)
其規(guī)則是當(dāng)前對(duì)象與o對(duì)象進(jìn)行比較,其返回一個(gè)int值,系統(tǒng)根據(jù)此值來(lái)進(jìn)行排序。
如 當(dāng)前對(duì)象>o對(duì)象,則返回值>0;(可將返回值定義為1)
如 當(dāng)前對(duì)象=o對(duì)象,則返回值=0;
如 當(dāng)前對(duì)象<o對(duì)象,則返回值〈0。(可將返回值定義為-1)
看TestArraylist的java代碼。
我們通過(guò)返回值1和-1位置的調(diào)換來(lái)實(shí)現(xiàn)升序和降序排列的轉(zhuǎn)換。
② java.util下有一個(gè)Comparator(比較器)
它擁有compare(),用來(lái)比較兩個(gè)方法。
要生成比較器,則用Sort中Sort(List,List(Compate))
第二種方法更靈活,且在運(yùn)行的時(shí)候不用編譯。
注意:要想實(shí)現(xiàn)comparTo()就必須在主方法中寫上implement comparable.
集合的最大缺點(diǎn)是無(wú)法進(jìn)行類型判定(這個(gè)缺點(diǎn)在JAVA1.5中已經(jīng)解決),這樣就可能出現(xiàn)因?yàn)轭愋筒煌霈F(xiàn)類型錯(cuò)誤。
解決的方法是添加類型的判斷。
而在LinkedList的底層是一種雙向循環(huán)鏈表。在此鏈表上每一個(gè)數(shù)據(jù)節(jié)點(diǎn)都由三部分組成:前指針(指向前面的節(jié)點(diǎn)的位置),數(shù)據(jù),后指針(指向后面的節(jié)點(diǎn)的位置)。最后一個(gè)節(jié)點(diǎn)的后指針指向第一個(gè)節(jié)點(diǎn)的前指針,形成一個(gè)循環(huán)。
LinkedList經(jīng)常用在增刪操作較多而查詢操作很少的情況下:隊(duì)列和堆棧。
隊(duì)列:先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。
堆棧:后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。
注意:使用堆棧的時(shí)候一定不能提供方法讓不是最后一個(gè)元素的元素獲得出棧的機(jī)會(huì)。
LinkedList提供以下方法:(ArrayList無(wú)此類方法)
addFirst();
removeFirst();
addLast();
removeLast();
Push用addFirst();pop用removeFirst(),實(shí)現(xiàn)后進(jìn)先出。
用isEmpty()--其父類的方法,來(lái)判斷棧是否為空。
在隊(duì)列中,put為入隊(duì)列操作,get為出隊(duì)列操作。
Put用addFirst(),get用removeLast()實(shí)現(xiàn)隊(duì)列。
List接口的實(shí)現(xiàn)類(Vector)(與ArrayList相似,區(qū)別是Vector是重量級(jí)的組件,使用使消耗的資源比較多。)
結(jié)論:在考慮并發(fā)的情況下用Vector(保證線程的安全)。
在不考慮并發(fā)的情況下用ArrayList(不能保證線程的安全)。
面試經(jīng)驗(yàn)(知識(shí)點(diǎn)):
java.util.stack(stack即為堆棧)的父類為Vector??墒莝tack的父類是最不應(yīng)該為Vector的。因?yàn)閂ector的底層是數(shù)組,且Vector有g(shù)et方法(意味著它可能訪問(wèn)到并不屬于最后一個(gè)位置元素的其他元素,很不安全)。
對(duì)于堆棧和隊(duì)列只能用push類和get類。
Stack類以后不要輕易使用。
?。?!實(shí)現(xiàn)堆棧一定要用LinkedList。
(在JAVA1.5中,collection有queue來(lái)實(shí)現(xiàn)隊(duì)列。)
集合類
集合類的對(duì)象是用來(lái)管理其他若干對(duì)象的,它類似于C++標(biāo)準(zhǔn)模板庫(kù)中的容器,不過(guò)在JAVA的集合類的對(duì)象中可以用來(lái)存放多種類型的對(duì)象。
接口和類共同構(gòu)成了一個(gè)集合框架,集合的概念,一個(gè)對(duì)象可以裝載多個(gè)對(duì)象,這個(gè)對(duì)象就是集合對(duì)象。
集合框架
1,接口
Collection 用來(lái)管理多個(gè)對(duì)象,集合中的每個(gè)元素都是對(duì)象。
Map,Map中沒(méi)有對(duì)象,而是鍵值對(duì),由Key,value組成的鍵值對(duì),Key是不可重復(fù)的。value是可以相同的,一個(gè)Key和一個(gè)value一一對(duì)應(yīng)。
集合中用到的類,接口在java.util包中,在使用時(shí)注意將其引入import。
Collection 接口(以下介紹其子接口)
1)List 一個(gè)List的實(shí)現(xiàn)類的對(duì)象在管理多個(gè)對(duì)象時(shí)會(huì)按順序組織對(duì)象(即按照將對(duì)象放入的順序存儲(chǔ)),List實(shí)現(xiàn)類的對(duì)象是由順序的。(注意,順序和排序的區(qū)別)
2)Set 一個(gè)Set的實(shí)現(xiàn)類表示一個(gè)數(shù)學(xué)概念上的集合,
Set的實(shí)現(xiàn)類的對(duì)象中的元素是無(wú)順序的,也就是不會(huì)按照輸入順序來(lái)存放,Set的實(shí)現(xiàn)類對(duì)象中的元素是不重復(fù)的。
3)SortedSet,他是Set的子接口,他的實(shí)現(xiàn)類會(huì)對(duì)集合中的元素進(jìn)行排序。但是要指定排序規(guī)則,他會(huì)按排序規(guī)則進(jìn)行排序。
Map 接口(以下介紹其子接口)
SortedMap,這個(gè)接口的實(shí)現(xiàn)類同樣可以實(shí)現(xiàn),不過(guò)是對(duì)鍵值對(duì)中的Key進(jìn)行排序,這個(gè)接口的實(shí)現(xiàn)類也是要指定排序規(guī)
則的。
JDK1.4中的集合是不安全的對(duì)象,JDK5.0中解決了這個(gè)問(wèn)題。
List接口的實(shí)現(xiàn)類
1> ArrayList是接近于功能的集合類,
ArryList的實(shí)質(zhì)就是一個(gè)會(huì)自動(dòng)增長(zhǎng)的數(shù)組,ArrayList是用封裝的數(shù)組來(lái)實(shí)現(xiàn)的List接口的。Collection的實(shí)現(xiàn)類對(duì)象的遍歷方式是用迭代來(lái)實(shí)現(xiàn)的。
在使用迭代器時(shí)先要活得一個(gè)迭代器的對(duì)象,Iterator(迭代器接口)這是一個(gè)接口,迭代器是在集合類中實(shí)現(xiàn)的,也
就是說(shuō),他是一個(gè)內(nèi)部類(匿名內(nèi)部類)實(shí)現(xiàn)的。
Iterator接口中定義的常用方法方法hasNext(),next()。
hasNext(),這個(gè)方法會(huì)使用一個(gè)游標(biāo),并通過(guò)判斷游標(biāo)指向的位置是否存放有對(duì)象。
next()方法也是Iterator接口中定義好的方法,這個(gè)方法會(huì)使游標(biāo)指向下一個(gè)元素的位置,游標(biāo)會(huì)跳過(guò)第一個(gè)元素,并
返回其中的內(nèi)容。
Collections 這是一個(gè)工具類,也是java.util包中的,這個(gè)類中的sort(list接口的實(shí)現(xiàn)類的對(duì)象)方法,其參數(shù)是一個(gè)集合類的對(duì)象,這個(gè)方法使用來(lái)對(duì)集合類的對(duì)象進(jìn)行排序的。以后,我將以集合這個(gè)名字來(lái)稱呼集合類的對(duì)象。,對(duì)于
字符串對(duì)象內(nèi)容的集合來(lái)說(shuō)會(huì)按字典順序排序(升序),對(duì)于數(shù)字內(nèi)容的集合排序也會(huì)按照升序排序。
排序可一份為兩部分內(nèi)容,一個(gè)是排序的規(guī)則,也就是按照什么來(lái)進(jìn)行排序,并且排成什么樣的順序。
第二個(gè)就是排序的算法,他決定了排序的效率。
在對(duì)自定義的集合內(nèi)容類型排序時(shí),需要先定義那個(gè)類型的排序規(guī)則。
Comparable接口,這個(gè)接口中只定義了一個(gè)compareTo(Object o),方法的返回至類型是整型,如果當(dāng)前對(duì)象大于參數(shù)對(duì)象就返回正數(shù),當(dāng)前對(duì)象等于參數(shù)對(duì)象是就返回0,當(dāng)前對(duì)象小于參數(shù)對(duì)象時(shí)就返回負(fù)值,這樣寫就是升序排列,反之則是進(jìn)行降序排列,在實(shí)現(xiàn)這個(gè)接口中的方法時(shí),返回值定義方式,只有這兩種。
根據(jù)指定類型的排序規(guī)則實(shí)現(xiàn)了Comparable接口,那么就可以對(duì)存有這個(gè)類型的集合進(jìn)行整體排序。Comparable接口,
也叫做可比較接口。這個(gè)接口在java.lang包下。
只要實(shí)現(xiàn)了這個(gè)接口,就是可排序的。
接下來(lái)介紹
另外一種對(duì)自定義類型對(duì)象的集合整體排序的方法,
也就是實(shí)現(xiàn)比較器接口(Comparator),這個(gè)接口中定義了一個(gè)compare(Object o1,Object o2)方法來(lái)比較兩個(gè)對(duì)象,這個(gè)方法的返回值定義和上面介紹的那個(gè)方法是一樣。注意:在API,幫助文檔中
以上兩個(gè)方法的參數(shù)類型是T,這代表的模板類型,也就是集合中存放的內(nèi)容的類型,在JDK1.4中其參數(shù)就是Object類型,模板類型的詳細(xì)內(nèi)容會(huì)在最后的JDK5.0新特性中講到。
Comparator接口可以在匿名內(nèi)部類中實(shí)現(xiàn),Collections 中的sort(集合了的對(duì)象,比較器)方法,可以對(duì)自定義類型內(nèi)容的集合進(jìn)行整體排序。
2>LinkedList,它是List接口的實(shí)現(xiàn)類,
其底層是用雙向循環(huán)鏈表來(lái)實(shí)現(xiàn)的。
注意:ArrayList的查詢效率比較高,增刪動(dòng)作的效率比較差,適用于查詢比較頻繁,增刪動(dòng)作較少的元素管理的集合。
? ? ?
LinkedList的查詢效率低,但是增刪效率很高。適用于增刪動(dòng)作的比較頻繁,查詢次數(shù)較少的元素管理集合。
ArrayList,LinkedList都是線程不安全的。
實(shí)現(xiàn)堆棧 1,數(shù)組(ArrayList,增刪效率比較低,不適合)
? ? ? ? 2,
LinkedList(實(shí)現(xiàn)堆棧的好方法)? ? ? ? 3,java.util.Stack類,Stack是Vector的子類,Vector類是一個(gè)線程安全的(是一個(gè)重量級(jí)的類),并繼承
了Vector的方法,Verctor類和ArrayList的功能近乎相同。(不推薦使用Stack類來(lái)
實(shí)現(xiàn)堆棧)。
Set接口的實(shí)現(xiàn)類
HashSet
Set的實(shí)現(xiàn)類的集合對(duì)象中不能夠有重復(fù)元素,HashSet也一樣他是使用了一種標(biāo)識(shí)來(lái)確定元素的不重復(fù),HashSet用一種算法來(lái)保證HashSet中的元素是不重復(fù)的,
HashSet的底層實(shí)現(xiàn)還是數(shù)組。
Object類中的hashCode()的方法是所有子類都會(huì)繼承這個(gè)方法,這個(gè)方法會(huì)用Hash算法算出一個(gè)Hash(哈希)碼值返回,HashSet會(huì)用Hash碼值去和數(shù)組長(zhǎng)度取模,模(這個(gè)模就是對(duì)象要存放在數(shù)組中的位置)相同時(shí)才會(huì)判斷數(shù)組中的元素和要加入的對(duì)象的內(nèi)容是否相同,如果不同才會(huì)添加進(jìn)去。
Hash算法是一種散列算法。
注意:
所以要存入HashSet的集合對(duì)象中的自定義類必須覆蓋hashCode(),equals()兩個(gè)方法,才能保證集合中元素容不重復(fù)。在覆蓋和hashCode()方法時(shí),要使相同對(duì)象的hashCode()方法返回相同值,覆蓋equals()方法再判斷其內(nèi)容。為了保證效率,所以在覆蓋hashCode()方法時(shí),也要盡量使不同對(duì)象盡量返回不同的Hash碼值。
如果數(shù)組中的元素和要加入的對(duì)象的hashCode()返回了相同的Hash值(相同對(duì)象),才會(huì)用equals()方法來(lái)判斷兩個(gè)對(duì)象的內(nèi)容是否相同。
SortedSet接口是Set的子接口。
TreeSet是SortedSet接口的實(shí)現(xiàn)類,他可以對(duì)集合中的元素進(jìn)行排序。
要存放在TreeSet中自定義類的對(duì)象,這個(gè)類要么是已經(jīng)實(shí)現(xiàn)了Comparable接口,要么是能給出Comparator比較器,TreeSet可以自動(dòng)過(guò)濾掉重復(fù)元素所以不用重載hashCode()方法,TreeSet會(huì)根據(jù)比較規(guī)則判斷元素內(nèi)容是否相同,TreeSet會(huì)在元素存入世就進(jìn)行了排序。(在TreeSet給出排序規(guī)則時(shí),一定要注意對(duì)象內(nèi)容相等的條件,一定要注意在主觀的認(rèn)為兩個(gè)對(duì)象內(nèi)容相同時(shí),才可以使用比較少的條件來(lái)進(jìn)行判斷)
在
要排序時(shí)才使用TreeSet類(存儲(chǔ)效率比較低),HashSet的存儲(chǔ)效率比較高,在需要為HashSet的對(duì)象排序時(shí),就可以把HashSet中的元素放入TreeSet。
通向財(cái)富的道路有千萬(wàn)條,但條條道路都驚人地相似。而且令人詫異的是,拼命積累財(cái)富的男人也驚人地相似,他們?cè)谛睦硖卣魃暇拖袷峭粋€(gè)模子壓出來(lái)的一樣。
??? 百萬(wàn)富翁有諸多特征,絕大多數(shù)白手起家的百萬(wàn)富翁都是出身貧窮的人,積累金錢對(duì)于那些一貧如洗的人有著不可抗拒的力量。
??? 這些男人是很有冒險(xiǎn)精神的,他們敢于冒那些腳踏實(shí)地、像苦工似地掙錢的男人所不敢涉足的風(fēng)險(xiǎn)。他們是精明強(qiáng)干的,他們很有遠(yuǎn)見(jiàn)卓識(shí);他們的天才在于能知道如何利用別人的主意來(lái)賺錢。這是賺錢的真正秘訣——利用別人創(chuàng)造性的思想,并且把它們運(yùn)用到實(shí)際中去。
??? 這樣的男人很容易和別人打成一片。他們很有洞察力,他們會(huì)觀察別人,知道如何通過(guò)與別人打交道來(lái)獲得他們所需要的東西,也知道別人對(duì)他們的反應(yīng)如何。追求財(cái)富的人內(nèi)心深處有著強(qiáng)烈的孤獨(dú)感,但他們并不因此而去追求政治上的名望和成就。賺錢是他們絕對(duì)全神貫注的追求。這給他們勝于一切的最大滿足和快樂(lè)。
??? 1、讓金錢成為你的情人。
??? 別在性愛(ài)上浪費(fèi)時(shí)間和精力,你會(huì)發(fā)現(xiàn),從長(zhǎng)遠(yuǎn)來(lái)看,賺錢比性愛(ài)更讓人興奮,這可不是空頭支票。尋求需求,滿足需求。追求財(cái)富者的最大天分是瞄準(zhǔn)時(shí)機(jī),預(yù)測(cè)所需。大眾牌小汽車,美國(guó)快餐食品都是滿足人們需要的例子。
??? 2、謹(jǐn)防從眾心理。
??? 群眾心理弊病多,即使它是正確的,追隨它一般也沒(méi)有什么利潤(rùn)。在一般情況下,摘取經(jīng)濟(jì)精華的都是帶頭人而不是追隨者。
??? 3、當(dāng)雇主而不是雇員。
??? 那些滿足于雇主付給他們高薪的男人并不是真正追求財(cái)富的人,他們的目標(biāo)僅僅是成就感或權(quán)力欲。你最好去當(dāng)老板,即使雇員只有你一個(gè),賺的錢也會(huì)比任何一個(gè)公司付給你的多。
??? 4、發(fā)展你的支配技巧。
??? 大多數(shù)人認(rèn)為,支配別人,讓他們?nèi)プ瞿阆胱屗麄兏傻氖虑榉浅毫印H欢?,?shí)際情況是,我們時(shí)時(shí)都在自己沒(méi)有意識(shí)到的情況下支配著別人。
??? 5、追求財(cái)富的人常常是直覺(jué)地支配著別人,而且是個(gè)行家里手。
?
????????下午答辯,還算順利,雖然花了半天時(shí)間,沒(méi)任何技術(shù)含量,但是終于解脫了...
?????????五天來(lái)平均每天也就睡了不到兩小時(shí),現(xiàn)在真有點(diǎn)頂不住了(意識(shí)都模糊了),但是為了把生物鐘調(diào)整過(guò)來(lái),還是撐一會(huì)吧。突然閑下來(lái)卻不知道做點(diǎn)什么,20號(hào)有個(gè)面試,等會(huì)沒(méi)事把Struts那本書(shū)再看看。
?????????接下來(lái)都不知道寫些什么東西,就把實(shí)踐中的一個(gè)弄明白的一個(gè)小知識(shí)記一下:
在JDBC中,對(duì)于游標(biāo)對(duì)象ResultSet的getString()方法也是用游標(biāo)的模式來(lái)讀取數(shù)據(jù)的(不知道這樣描敘對(duì)不對(duì)),即,你要取字段1,字段2,就必須先調(diào)用rs.getString(1),再調(diào)用rs.getString(2),不能反過(guò)來(lái),否則不能正確讀取。原因還不知道,有空了研究一下。
今天成績(jī)都出來(lái)了,結(jié)果還是比較理想的,以后會(huì)相對(duì)輕松些。
更新日志都是半個(gè)月前的事了,當(dāng)時(shí)還說(shuō)以后每天都更新,真是慚愧
這幾天都是忙考試,弄得心力憔悴,今天晚上終于閑下來(lái),繼續(xù)寫實(shí)踐的程序。
剛剛解決了前幾個(gè)小時(shí)的困惑,現(xiàn)在對(duì)JAVA的內(nèi)部類機(jī)制又有了更新的認(rèn)識(shí)(興奮中
......),廢話少說(shuō),來(lái)看代碼:
package tanzhang;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import com.swtdesigner.SWTResourceManager;
import org.eclipse.swt.widgets.TabItem;
public class tanzhang {
?/**
? * Launch the application
? * @param args
? */
?private static Table table;
?private static TableItem newItemTableItem;
?
?public static void main(String[] args) {
??final Display display = Display.getDefault();
??final Shell shell = new Shell();
??shell.setImage(SWTResourceManager.getImage(tanzhang.class, "/org/eclipse/ui/internal/forms/widgets/progress.gif"));
??shell.setSize(774, 514);
??shell.setText("宣城供電局消弧線圈臺(tái)帳");
............
............
............
??final Combo combo_1 = new Combo(composite, SWT.READ_ONLY);
??combo_1.addSelectionListener(new SelectionAdapter() {
???public void widgetSelected(SelectionEvent arg0) {
????String str=combo_1.getText();
????try{?
?????String dbUrl = "jdbc:odbc:test";
?????Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
?????Connection conn = DriverManager.getConnection(dbUrl, "", "");
?????Statement stmt=conn.createStatement();
?????String sql="SELECT * from test where 單元名稱="+str;
?????ResultSet rs=stmt.executeQuery(sql);
?????
?????if(rs.next()){
?????for(int i=0;i<=4;i++){?????????
???????newItemTableItem.setText(i,rs.getString(i+1));??
???????}
?????
?????rs.close();
?????stmt.close();
?????conn.close();
?????}
????}catch(Exception e){}
????
???}
??});
............
............
............
??table = new Table(composite_1, SWT.VIRTUAL | SWT.FULL_SELECTION | SWT.BORDER | SWT.HIDE_SELECTION);
??table.setLinesVisible(true);
??table.setHeaderVisible(true);
??table.setBounds(0, 0, 678, 80);
............
............
............
???剛開(kāi)始是用final修飾table和newItemTableItem,但是“newItemTableItem.setText(i,rs.getString(i+1));”這行(代碼中蘭色那句)會(huì)報(bào)錯(cuò),編譯器提示“無(wú)法解析newItemTableItem”。我的第一反應(yīng)是監(jiān)聽(tīng)器的位置放得不對(duì)。翻了一下書(shū),說(shuō)SWT/JFace事件代碼中要訪問(wèn)類中變量有三種方法:(1)加final;(2)將變量變?yōu)轭惖膶?shí)例變量;(3)將事件代碼寫成命名內(nèi)部類,然后通過(guò)構(gòu)造函數(shù)的參數(shù)來(lái)傳入。
???想了想好象只有第三種方法值得試試,但是后來(lái)想起來(lái)newItemTableItem是個(gè)對(duì)象,傳入以后卻不知道怎么轉(zhuǎn)換類型,呆了...
???最后做了個(gè)新的程序試了試,在新程序里是成功的,就是要把newItemTableItem在main函數(shù)外申明其為private static,但是在舊的程序中,編譯器是通過(guò)了,但是進(jìn)行連接數(shù)據(jù)庫(kù)查詢的時(shí)候,出現(xiàn)“Fatal Exception...”的錯(cuò)誤,當(dāng)時(shí)一下就蒙掉了?。⊥耆粯拥模趺磿?huì)不成功。
???百般無(wú)奈以后,----當(dāng)然之前肯定是深思熟慮----終于腦海中閃出了傳說(shuō)中的“靈感”!于是馬上把table也定義為private static......OK!???勝利總是來(lái)得那么突然,那瞬間的感覺(jué)就象90分鐘打進(jìn)的金球...
???當(dāng)然到現(xiàn)在,我還沒(méi)找到確切的原因。只能用自己的話總結(jié)一下:(1)匿名內(nèi)部類中的方法是不能訪問(wèn)其他方法中定義的變量的(包括實(shí)例對(duì)象),要訪問(wèn)必須在類中將其定義為static類型。(2)TableItem是Table的子控件,要定義TableItem為static也要同時(shí)把Table定義為static類型。
???給自己一個(gè)任務(wù):研究JAVA的內(nèi)部類機(jī)制;研究SWT中父子控件關(guān)系的機(jī)制!
???看看《Thinking in JAVA》或許會(huì)是個(gè)不錯(cuò)的選擇,當(dāng)然還有《The Definitive Guide to SWT and JFace》。
?
昨天終于考完了!
都大四了,還用最變態(tài)的老師(三大魔頭)來(lái)折磨我們,對(duì)于這學(xué)校真無(wú)語(yǔ)了......
現(xiàn)在終于可以繼續(xù)我的自由學(xué)習(xí)之旅,以后一定每天上來(lái)更新blog。
時(shí)間不多了,既然已經(jīng)飽受過(guò)往昏昏惑惑之疾、因循茍且之痛,那么現(xiàn)在就要珍惜分分秒秒,彌補(bǔ)差距。
今天下午花了兩個(gè)小時(shí)把XML SPY教程學(xué)了一遍(沒(méi)想到這么快能搞定),我用的是2007,學(xué)的是2005,但基本上沒(méi)區(qū)別,進(jìn)度還是蠻快的。最后估計(jì)那個(gè)xml
schema文件沒(méi)寫好,最后用xslt轉(zhuǎn)換沒(méi)成功。不過(guò)晚上準(zhǔn)備再試一次,權(quán)當(dāng)做復(fù)習(xí)。
以前都用記事本寫xml,現(xiàn)在用了xml spy感覺(jué)真是太好用了,尤其是xml數(shù)據(jù)與數(shù)據(jù)庫(kù)的互相轉(zhuǎn)換。而且還可以用工程來(lái)管理(還沒(méi)試呢),對(duì)于開(kāi)發(fā)大一點(diǎn)的程序無(wú)疑方便了許多。
晚上再準(zhǔn)備把struts那本書(shū)看看。
對(duì)了,今天元旦,祝大家新年快樂(lè),祈禱家人在新的一年里平平安安!
?????????昨天搞了一個(gè)下午加晚上,配置eclipse3.2及其插件,最后太晚了沒(méi)上來(lái)更新blog。有幾點(diǎn)需要記一下:
??????(1)其實(shí)在links目錄里設(shè)置插件路徑的時(shí)候“//”也是可以用的,網(wǎng)上一般說(shuō)用“/”或“\\”。以后只要記住一點(diǎn):“\”(windows里的路徑格式)不能用就行了。
??????(2)myeclipse5.0以上必須配eclipse3.2;myeclipse4.0配eclipse3.0; 而myeclipse4.1才能配eclipse3.1。 昨天剛開(kāi)始沒(méi)搞明白,給我的eclipse3.1.2下了一個(gè)myeclipse4.0和一個(gè)myeclipse5.1結(jié)果都不行,最后索性下了個(gè)最新的eclipse3.2.1,心想反正早晚都要升級(jí)干脆現(xiàn)在提前搞定算了。
??????(3)關(guān)于eclipse3.2.1的lomboz插件問(wèn)題,在網(wǎng)上看了很多帖子(其實(shí)源帖就兩篇),總結(jié)如此:a) all-in-one版本里其實(shí)已經(jīng)包含了eclipse3.2了;b)兩種選擇:直接下載org.objectweb.lomboz-all-in-one-win32解壓縮即可;據(jù)說(shuō)為了能支持GEF要分別下載這兩個(gè)插件lomboz-S-3.2RC2-200608081203和lomboz-and-prereqs-S-3.2RC2-200608081203進(jìn)行安裝。? 我最后還是選擇了第二種方法。
????????其實(shí)有了myeclipse,lomboz可以不用裝了,我想我只是為了多一種選擇吧,不知道這是不是我骨子里已經(jīng)開(kāi)始傾向于開(kāi)源了:)
??????(4)最后下了一個(gè)javasript的eclipse小插件,但是安裝不成功,不知道什么原因,反正以后再試吧...
??????最后,想說(shuō)一點(diǎn),其實(shí)昨天一天的工作是毫無(wú)技術(shù)含量的 ,而且現(xiàn)在時(shí)間這么緊,我應(yīng)該把一些更重要或更有價(jià)值的事情提到議程之前來(lái),而不是跟自己過(guò)不去死鉆牛角尖。我知道這是性格做慫,但是希望以后自己更理智點(diǎn)更“聰明”點(diǎn)。時(shí)間對(duì)于我們這樣的人是最寶貴的!
多數(shù) Web 應(yīng)用程序都使用請(qǐng)求/響應(yīng)模型從服務(wù)器上獲得完整的 HTML 頁(yè)面。常常是點(diǎn)擊一個(gè)按鈕,等待服務(wù)器響應(yīng),再點(diǎn)擊另一個(gè)按鈕,然后再等待,這樣一個(gè)反復(fù)的過(guò)程。有了 Ajax 和 XMLHttpRequest 對(duì)象,就可以使用不必讓用戶等待服務(wù)器響應(yīng)的請(qǐng)求/響應(yīng)模型了。本文中,Brett McLaughlin 介紹了如何創(chuàng)建能夠適應(yīng)不同瀏覽器的 XMLHttpRequest 實(shí)例,建立和發(fā)送請(qǐng)求,并響應(yīng)服務(wù)器。
本系列的上一期文章(請(qǐng)參閱 參考資料 中的鏈接),我們介紹了 Ajax 應(yīng)用程序,考察了推動(dòng) Ajax 應(yīng)用程序的基本概念。其中的核心是很多您可能已經(jīng)了解的技術(shù):JavaScript、HTML 和 XHTML、一點(diǎn)動(dòng)態(tài) HTML 以及 DOM(文檔對(duì)象模型)。本文將放大其中的一點(diǎn),把目光放到具體的 Ajax 細(xì)節(jié)上。
本文中,您將開(kāi)始接觸最基本和基礎(chǔ)性的有關(guān) Ajax 的全部對(duì)象和編程方法:XMLHttpRequest 對(duì)象。該對(duì)象實(shí)際上僅僅是一個(gè)跨越所有 Ajax 應(yīng)用程序的公共線程,您可能已經(jīng)預(yù)料到,只有徹底理解該對(duì)象才能充分發(fā)揮編程的潛力。事實(shí)上,有時(shí)您會(huì)發(fā)現(xiàn),要正確地使用 XMLHttpRequest,顯然不能 使用 XMLHttpRequest。這到底是怎么回事呢?
Web 2.0 一瞥
在深入研究代碼之前首先看看最近的觀點(diǎn) —— 一定要十分清楚 Web 2.0 這個(gè)概念。聽(tīng)到 Web 2.0 這個(gè)詞的時(shí)候,應(yīng)該首先問(wèn)一問(wèn) “Web 1.0 是什么?” 雖然很少聽(tīng)人提到 Web 1.0,實(shí)際上它指的就是具有完全不同的請(qǐng)求和響應(yīng)模型的傳統(tǒng) Web。比如,到 Amazon.com 網(wǎng)站上點(diǎn)擊一個(gè)按鈕或者輸入搜索項(xiàng)。就會(huì)對(duì)服務(wù)器發(fā)送一個(gè)請(qǐng)求,然后響應(yīng)再返回到瀏覽器。該請(qǐng)求不僅僅是圖書(shū)和書(shū)目列表,而是另一個(gè)完整的 HTML 頁(yè)面。因此當(dāng) Web 瀏覽器用新的 HTML 頁(yè)面重繪時(shí),可能會(huì)看到閃爍或抖動(dòng)。事實(shí)上,通過(guò)看到的每個(gè)新頁(yè)面可以清晰地看到請(qǐng)求和響應(yīng)。
Web 2.0(在很大程度上)消除了這種看得見(jiàn)的往復(fù)交互。比如訪問(wèn) Google Maps 或 Flickr 這樣的站點(diǎn)(到這些支持 Web 2.0 和 Ajax 站點(diǎn)的鏈接請(qǐng)參閱 參考資料)。比如在 Google Maps 上,您可以拖動(dòng)地圖,放大和縮小,只有很少的重繪操作。當(dāng)然這里仍然有請(qǐng)求和響應(yīng),只不過(guò)都藏到了幕后。作為用戶,體驗(yàn)更加舒適,感覺(jué)很像桌面應(yīng)用程序。這種新的感受和范型就是當(dāng)有人提到 Web 2.0 時(shí)您所體會(huì)到的。
需要關(guān)心的是如何使這些新的交互成為可能。顯然,仍然需要發(fā)出請(qǐng)求和接收響應(yīng),但正是針對(duì)每次請(qǐng)求/響應(yīng)交互的 HTML 重繪造成了緩慢、笨拙的 Web 交互的感受。因此很清楚,我們需要一種方法使發(fā)送的請(qǐng)求和接收的響應(yīng)只 包含需要的數(shù)據(jù)而不是整個(gè) HTML 頁(yè)面。惟一需要獲得整個(gè)新 HTML 頁(yè)面的時(shí)候就是希望用戶看到 新頁(yè)面的時(shí)候。
但多數(shù)交互都是在已有頁(yè)面上增加細(xì)節(jié)、修改主體文本或者覆蓋原有數(shù)據(jù)。這些情況下,Ajax 和 Web 2.0 方法允許在不 更新整個(gè) HTML 頁(yè)面的情況下發(fā)送和接收數(shù)據(jù)。對(duì)于那些經(jīng)常上網(wǎng)的人,這種能力可以讓您的應(yīng)用程序感覺(jué)更快、響應(yīng)更及時(shí),讓他們不時(shí)地光顧您的網(wǎng)站。
XMLHttpRequest 簡(jiǎn)介
要真正實(shí)現(xiàn)這種絢麗的奇跡,必須非常熟悉一個(gè) JavaScript 對(duì)象,即 XMLHttpRequest。這個(gè)小小的對(duì)象實(shí)際上已經(jīng)在幾種瀏覽器中存在一段時(shí)間了,它是本專欄今后幾個(gè)月中要介紹的 Web 2.0、Ajax 和大部分其他內(nèi)容的核心。為了讓您快速地大體了解它,下面給出將要用于該對(duì)象的很少的幾個(gè) 方法和屬性。
- open():建立到服務(wù)器的新請(qǐng)求。
- send():向服務(wù)器發(fā)送請(qǐng)求。
- abort():退出當(dāng)前請(qǐng)求。
- readyState:提供當(dāng)前 HTML 的就緒狀態(tài)。
- responseText:服務(wù)器返回的請(qǐng)求響應(yīng)文本。
如果不了解這些(或者其中的任何 一個(gè)),您也不用擔(dān)心,后面幾篇文章中我們將介紹每個(gè)方法和屬性?,F(xiàn)在應(yīng)該 了解的是,明確用 XMLHttpRequest 做什么。要注意這些方法和屬性都與發(fā)送請(qǐng)求及處理響應(yīng)有關(guān)。事實(shí)上,如果看到 XMLHttpRequest 的所有方法和屬性,就會(huì)發(fā)現(xiàn)它們都 與非常簡(jiǎn)單的請(qǐng)求/響應(yīng)模型有關(guān)。顯然,我們不會(huì)遇到特別新的 GUI 對(duì)象或者創(chuàng)建用戶交互的某種超極神秘的方法,我們將使用非常簡(jiǎn)單的請(qǐng)求和非常簡(jiǎn)單的響應(yīng)。聽(tīng)起來(lái)似乎沒(méi)有多少吸引力,但是用好該對(duì)象可以徹底改變您的應(yīng)用程序。
簡(jiǎn)單的 new
首先需要?jiǎng)?chuàng)建一個(gè)新變量并賦給它一個(gè) XMLHttpRequest 對(duì)象實(shí)例。這在 JavaScript 中很簡(jiǎn)單,只要對(duì)該對(duì)象名使用 new 關(guān)鍵字即可,如 清單 1 所示。
清單 1. 創(chuàng)建新的 XMLHttpRequest 對(duì)象
<script language="javascript" type="text/javascript">
var request = new XMLHttpRequest();
</script>
|
不難吧?記住,JavaScript 不要求指定變量類型,因此不需要像 清單 2 那樣做(在 Java 語(yǔ)言中可能需要這樣)。
清單 2. 創(chuàng)建 XMLHttpRequest 的 Java 偽代碼
XMLHttpRequest request = new XMLHttpRequest();
|
因此在 JavaScript 中用 var 創(chuàng)建一個(gè)變量,給它一個(gè)名字(如 “request”),然后賦給它一個(gè)新的 XMLHttpRequest 實(shí)例。此后就可以在函數(shù)中使用該對(duì)象了。
錯(cuò)誤處理
在實(shí)際上各種事情都可能出錯(cuò),而上面的代碼沒(méi)有提供任何錯(cuò)誤處理。較好的辦法是創(chuàng)建該對(duì)象,并在出現(xiàn)問(wèn)題時(shí)優(yōu)雅地退出。比如,任何較早的瀏覽器(不論您是否相信,仍然有人在使用老版本的 Netscape Navigator)都不支持 XMLHttpRequest,您需要讓這些用戶知道有些地方出了問(wèn)題。清單 3 說(shuō)明如何創(chuàng)建該對(duì)象,以便在出現(xiàn)問(wèn)題的時(shí)候發(fā)出 JavaScript 警告。
清單 3. 創(chuàng)建具有錯(cuò)誤處理能力的 XMLHttpRequest
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (failed) {
request = false;
}
if (!request)
alert("Error initializing XMLHttpRequest!");
</script>
|
一定要理解這些步驟:
- 創(chuàng)建一個(gè)新變量 request 并賦值 false。后面將使用 false 作為判定條件,它表示還沒(méi)有創(chuàng)建 XMLHttpRequest 對(duì)象。
- 增加 try/catch 塊:
- 嘗試創(chuàng)建 XMLHttpRequest 對(duì)象。
- 如果失?。╟atch (failed))則保證 request 的值仍然為 false。
- 檢查 request 是否仍為 false(如果一切正常就不會(huì)是 false)。
- 如果出現(xiàn)問(wèn)題(request 是 false)則使用 JavaScript 警告通知用戶出現(xiàn)了問(wèn)題。
代碼非常簡(jiǎn)單,對(duì)大多數(shù) JavaScript 和 Web 開(kāi)發(fā)人員來(lái)說(shuō),真正理解它要比讀寫代碼花更長(zhǎng)的時(shí)間?,F(xiàn)在已經(jīng)得到了一段帶有錯(cuò)誤檢查的 XMLHttpRequest 對(duì)象創(chuàng)建代碼,還可以告訴您哪兒出了問(wèn)題。
應(yīng)付 Microsoft
看起來(lái)似乎一切良好,至少在用 Internet Explorer 試驗(yàn)這些代碼之前是這樣的。如果這樣試驗(yàn)的話,就會(huì)看到 圖 1 所示的糟糕情形。
圖 1. Internet Explorer 報(bào)告錯(cuò)誤
|
Microsoft 參與了嗎?
關(guān)于 Ajax 和 Microsoft 對(duì)該領(lǐng)域不斷增長(zhǎng)的興趣和參與已經(jīng)有很多文章進(jìn)行了介紹。事實(shí)上,據(jù)說(shuō) Microsoft 最新版本的 Internet Explorer —— version 7.0,將在 2006 年下半年推出 —— 將開(kāi)始直接支持 XMLHttpRequest,讓您使用 new 關(guān)鍵字代替所有的 Msxml2.XMLHTTP 創(chuàng)建代碼。但不要太激動(dòng),仍然需要支持舊的瀏覽器,因此跨瀏覽器代碼不會(huì)很快消失。 |
|
顯然有什么地方不對(duì)勁,而 Internet Explorer 很難說(shuō)是一種過(guò)時(shí)的瀏覽器,因?yàn)槿澜缬?70% 在使用 Internet Explorer。換句話說(shuō),如果不支持 Microsoft 和 Internet Explorer 就不會(huì)受到 Web 世界的歡迎!因此我們需要采用不同的方法處理 Microsoft 瀏覽器。
經(jīng)驗(yàn)證發(fā)現(xiàn) Microsoft 支持 Ajax,但是其 XMLHttpRequest 版本有不同的稱呼。事實(shí)上,它將其稱為幾種 不同的東西。如果使用較新版本的 Internet Explorer,則需要使用對(duì)象 Msxml2.XMLHTTP,而較老版本的 Internet Explorer 則使用 Microsoft.XMLHTTP。我們需要支持這兩種對(duì)象類型(同時(shí)還要支持非 Microsoft 瀏覽器)。請(qǐng)看看 清單 4,它在前述代碼的基礎(chǔ)上增加了對(duì) Microsoft 的支持。
清單 4. 增加對(duì) Microsoft 瀏覽器的支持
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
</script>
|
很容易被這些花括號(hào)迷住了眼睛,因此下面分別介紹每一步:
- 創(chuàng)建一個(gè)新變量 request 并賦值 false。使用 false 作為判斷條件,它表示還沒(méi)有創(chuàng)建 XMLHttpRequest 對(duì)象。
- 增加 try/catch 塊:
- 嘗試創(chuàng)建 XMLHttpRequest 對(duì)象。
- 如果失敗(catch (trymicrosoft)):
- 嘗試使用較新版本的 Microsoft 瀏覽器創(chuàng)建 Microsoft 兼容的對(duì)象(Msxml2.XMLHTTP)。
- 如果失敗(catch (othermicrosoft))嘗試使用較老版本的 Microsoft 瀏覽器創(chuàng)建 Microsoft 兼容的對(duì)象(Microsoft.XMLHTTP)。
- 如果失?。╟atch (failed))則保證 request 的值仍然為 false。
- 檢查 request 是否仍然為 false(如果一切順利就不會(huì)是 false)。
- 如果出現(xiàn)問(wèn)題(request 是 false)則使用 JavaScript 警告通知用戶出現(xiàn)了問(wèn)題。
這樣修改代碼之后再使用 Internet Explorer 試驗(yàn),就應(yīng)該看到已經(jīng)創(chuàng)建的表單(沒(méi)有錯(cuò)誤消息)。我實(shí)驗(yàn)的結(jié)果如 圖 2 所示。
圖 2. Internet Explorer 正常工作
靜態(tài)與動(dòng)態(tài)
再看一看清單 1、3 和 4,注意,所有這些代碼都直接嵌套在 script 標(biāo)記中。像這種不放到方法或函數(shù)體中的 JavaScript 代碼稱為靜態(tài) JavaScript。就是說(shuō)代碼是在頁(yè)面顯示給用戶之前的某個(gè)時(shí)候運(yùn)行。(雖然根據(jù)規(guī)范不能完全精確地 知道這些代碼何時(shí)運(yùn)行對(duì)瀏覽器有什么影響,但是可以保證這些代碼在用戶能夠與頁(yè)面交互之前運(yùn)行。)這也是多數(shù) Ajax 程序員創(chuàng)建 XMLHttpRequest 對(duì)象的一般方式。
就是說(shuō),也可以像 清單 5 那樣將這些代碼放在一個(gè)方法中。
清單 5. 將 XMLHttpRequest 創(chuàng)建代碼移動(dòng)到方法中
<script language="javascript" type="text/javascript">
var request;
function createRequest() {
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
}
</script>
|
如果按照這種方式編寫代碼,那么在處理 Ajax 之前需要調(diào)用該方法。因此還需要 清單 6 這樣的代碼。
清單 6. 使用 XMLHttpRequest 的創(chuàng)建方法
<script language="javascript" type="text/javascript">
var request;
function createRequest() {
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
}
function getCustomerInfo() {
createRequest();
// Do something with the request variable
}
</script>
|
此代碼惟一的問(wèn)題是推遲了錯(cuò)誤通知,這也是多數(shù) Ajax 程序員不采用這一方法的原因。假設(shè)一個(gè)復(fù)雜的表單有 10 或 15 個(gè)字段、選擇框等,當(dāng)用戶在第 14 個(gè)字段(按照表單順序從上到下)輸入文本時(shí)要激活某些 Ajax 代碼。這時(shí)候運(yùn)行 getCustomerInfo() 嘗試創(chuàng)建一個(gè) XMLHttpRequest 對(duì)象,但(對(duì)于本例來(lái)說(shuō))失敗了。然后向用戶顯示一條警告,明確地告訴他們不能使用該應(yīng)用程序。但用戶已經(jīng)花費(fèi)了很多時(shí)間在表單中輸入數(shù)據(jù)!這是非常令人討厭的,而討厭顯然不會(huì)吸引用戶再次訪問(wèn)您的網(wǎng)站。
如果使用靜態(tài) JavaScript,用戶在點(diǎn)擊頁(yè)面的時(shí)候很快就會(huì)看到錯(cuò)誤信息。這樣也很煩人,是不是?可能令用戶錯(cuò)誤地認(rèn)為您的 Web 應(yīng)用程序不能在他的瀏覽器上運(yùn)行。不過(guò),當(dāng)然要比他們花費(fèi)了 10 分鐘輸入信息之后再顯示同樣的錯(cuò)誤要好。因此,我建議編寫靜態(tài)的代碼,讓用戶盡可能早地發(fā)現(xiàn)問(wèn)題。
用 XMLHttpRequest 發(fā)送請(qǐng)求
得到請(qǐng)求對(duì)象之后就可以進(jìn)入請(qǐng)求/響應(yīng)循環(huán)了。記住,XMLHttpRequest 惟一的目的是讓您發(fā)送請(qǐng)求和接收響應(yīng)。其他一切都是 JavaScript、CSS 或頁(yè)面中其他代碼的工作:改變用戶界面、切換圖像、解釋服務(wù)器返回的數(shù)據(jù)。準(zhǔn)備好 XMLHttpRequest 之后,就可以向服務(wù)器發(fā)送請(qǐng)求了。
歡迎使用沙箱
Ajax 采用一種沙箱安全模型。因此,Ajax 代碼(具體來(lái)說(shuō)就是 XMLHttpRequest 對(duì)象)只能對(duì)所在的同一個(gè)域發(fā)送請(qǐng)求。以后的文章中將進(jìn)一步介紹安全和 Ajax,現(xiàn)在只要知道在本地機(jī)器上運(yùn)行的代碼只能對(duì)本地機(jī)器上的服務(wù)器端腳本發(fā)送請(qǐng)求。如果讓 Ajax 代碼在 www.breakneckpizza.com 上運(yùn)行,則必須 www.breakneck.com 中運(yùn)行的腳本發(fā)送請(qǐng)求。
設(shè)置服務(wù)器 URL
首先要確定連接的服務(wù)器的 URL。這并不是 Ajax 的特殊要求,但仍然是建立連接所必需的,顯然現(xiàn)在您應(yīng)該知道如何構(gòu)造 URL 了。多數(shù)應(yīng)用程序中都會(huì)結(jié)合一些靜態(tài)數(shù)據(jù)和用戶處理的表單中的數(shù)據(jù)來(lái)構(gòu)造該 URL。比如,清單 7 中的 JavaScript 代碼獲取電話號(hào)碼字段的值并用其構(gòu)造 URL。
清單 7. 建立請(qǐng)求 URL
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
}
</script>
|
這里沒(méi)有難懂的地方。首先,代碼創(chuàng)建了一個(gè)新變量 phone,并把 ID 為 “phone” 的表單字段的值賦給它。清單 8 展示了這個(gè)表單的 XHTML,其中可以看到 phone 字段及其 id 屬性。
清單 8. Break Neck Pizza 表單
<body>
<p><img src="breakneck-logo_4c.gif" alt="Break Neck Pizza" /></p>
<form action="POST">
<p>Enter your phone number:
<input type="text" size="14" name="phone" id="phone"
onChange="getCustomerInfo();" />
</p>
<p>Your order will be delivered to:</p>
<div id="address"></div>
<p>Type your order in here:</p>
<p><textarea name="order" rows="6" cols="50" id="order"></textarea></p>
<p><input type="submit" value="Order Pizza" id="submit" /></p>
</form>
</body>
|
還要注意,當(dāng)用戶輸入電話號(hào)碼或者改變電話號(hào)碼時(shí),將觸發(fā) 清單 8 所示的 getCustomerInfo() 方法。該方法取得電話號(hào)碼并構(gòu)造存儲(chǔ)在 url 變量中的 URL 字符串。記住,由于 Ajax 代碼是沙箱型的,因而只能連接到同一個(gè)域,實(shí)際上 URL 中不需要域名。該例中的腳本名為 /cgi-local/lookupCustomer.php。最后,電話號(hào)碼作為 GET 參數(shù)附加到該腳本中:"phone=" + escape(phone)。
如果以前沒(méi)用見(jiàn)過(guò) escape() 方法,它用于轉(zhuǎn)義不能用明文正確發(fā)送的任何字符。比如,電話號(hào)碼中的空格將被轉(zhuǎn)換成字符 %20,從而能夠在 URL 中傳遞這些字符。
可以根據(jù)需要添加任意多個(gè)參數(shù)。比如,如果需要增加另一個(gè)參數(shù),只需要將其附加到 URL 中并用 “與”(&)字符分開(kāi) [第一個(gè)參數(shù)用問(wèn)號(hào)(?)和腳本名分開(kāi)]。
打開(kāi)請(qǐng)求
|
open() 是打開(kāi)嗎?
Internet 開(kāi)發(fā)人員對(duì) open() 方法到底做什么沒(méi)有達(dá)成一致。但它實(shí)際上并不是 打開(kāi)一個(gè)請(qǐng)求。如果監(jiān)控 XHTML/Ajax 頁(yè)面及其連接腳本之間的網(wǎng)絡(luò)和數(shù)據(jù)傳遞,當(dāng)調(diào)用 open() 方法時(shí)將看不到任何通信。不清楚為何選用了這個(gè)名字,但顯然不是一個(gè)好的選擇。 |
|
有了要連接的 URL 后就可以配置請(qǐng)求了。可以用 XMLHttpRequest 對(duì)象的 open() 方法來(lái)完成。該方法有五個(gè)參數(shù):
- request-type:發(fā)送請(qǐng)求的類型。典型的值是 GET 或 POST,但也可以發(fā)送 HEAD 請(qǐng)求。
- url:要連接的 URL。
- asynch:如果希望使用異步連接則為 true,否則為 false。該參數(shù)是可選的,默認(rèn)為 true。
- username:如果需要身份驗(yàn)證,則可以在此指定用戶名。該可選參數(shù)沒(méi)有默認(rèn)值。
- password:如果需要身份驗(yàn)證,則可以在此指定口令。該可選參數(shù)沒(méi)有默認(rèn)值。
通常使用其中的前三個(gè)參數(shù)。事實(shí)上,即使需要異步連接,也應(yīng)該指定第三個(gè)參數(shù)為 “true”。這是默認(rèn)值,但堅(jiān)持明確指定請(qǐng)求是異步的還是同步的更容易理解。
將這些結(jié)合起來(lái),通常會(huì)得到 清單 9 所示的一行代碼。
清單 9. 打開(kāi)請(qǐng)求
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
}
|
一旦設(shè)置好了 URL,其他就簡(jiǎn)單了。多數(shù)請(qǐng)求使用 GET 就夠了(后面的文章中將看到需要使用 POST 的情況),再加上 URL,這就是使用 open() 方法需要的全部?jī)?nèi)容了。
挑戰(zhàn)異步性
本系列的后面一篇文章中,我將用很多時(shí)間編寫和使用異步代碼,但是您應(yīng)該明白為什么 open() 的最后一個(gè)參數(shù)這么重要。在一般的請(qǐng)求/響應(yīng)模型中,比如 Web 1.0,客戶機(jī)(瀏覽器或者本地機(jī)器上運(yùn)行的代碼)向服務(wù)器發(fā)出請(qǐng)求。該請(qǐng)求是同步的,換句話說(shuō),客戶機(jī)等待服務(wù)器的響應(yīng)。當(dāng)客戶機(jī)等待的時(shí)候,至少會(huì)用某種形式通知您在等待:
- 沙漏(特別是 Windows 上)。
- 旋轉(zhuǎn)的皮球(通常在 Mac 機(jī)器上)。
- 應(yīng)用程序基本上凍結(jié)了,然后過(guò)一段時(shí)間光標(biāo)變化了。
這正是 Web 應(yīng)用程序讓人感到笨拙或緩慢的原因 —— 缺乏真正的交互性。按下按鈕時(shí),應(yīng)用程序?qū)嶋H上變得不能使用,直到剛剛觸發(fā)的請(qǐng)求得到響應(yīng)。如果請(qǐng)求需要大量服務(wù)器處理,那么等待的時(shí)間可能很長(zhǎng)(至少在這個(gè)多處理器、DSL 沒(méi)有等待的世界中是如此)。
而異步請(qǐng)求不 等待服務(wù)器響應(yīng)。發(fā)送請(qǐng)求后應(yīng)用程序繼續(xù)運(yùn)行。用戶仍然可以在 Web 表單中輸入數(shù)據(jù),甚至離開(kāi)表單。沒(méi)有旋轉(zhuǎn)的皮球或者沙漏,應(yīng)用程序也沒(méi)有明顯的凍結(jié)。服務(wù)器悄悄地響應(yīng)請(qǐng)求,完成后告訴原來(lái)的請(qǐng)求者工作已經(jīng)結(jié)束(具體的辦法很快就會(huì)看到)。結(jié)果是,應(yīng)用程序感覺(jué)不 那么遲鈍或者緩慢,而是響應(yīng)迅速、交互性強(qiáng),感覺(jué)快多了。這僅僅是 Web 2.0 的一部分,但它是很重要的一部分。所有老套的 GUI 組件和 Web 設(shè)計(jì)范型都不能克服緩慢、同步的請(qǐng)求/響應(yīng)模型。
發(fā)送請(qǐng)求
一旦用 open() 配置好之后,就可以發(fā)送請(qǐng)求了。幸運(yùn)的是,發(fā)送請(qǐng)求的方法的名稱要比 open() 適當(dāng),它就是 send()。
send() 只有一個(gè)參數(shù),就是要發(fā)送的內(nèi)容。但是在考慮這個(gè)方法之前,回想一下前面已經(jīng)通過(guò) URL 本身發(fā)送過(guò)數(shù)據(jù)了:
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
|
雖然可以使用 send() 發(fā)送數(shù)據(jù),但也能通過(guò) URL 本身發(fā)送數(shù)據(jù)。事實(shí)上,GET 請(qǐng)求(在典型的 Ajax 應(yīng)用中大約占 80%)中,用 URL 發(fā)送數(shù)據(jù)要容易得多。如果需要發(fā)送安全信息或 XML,可能要考慮使用 send() 發(fā)送內(nèi)容(本系列的后續(xù)文章中將討論安全數(shù)據(jù)和 XML 消息)。如果不需要通過(guò) send() 傳遞數(shù)據(jù),則只要傳遞 null 作為該方法的參數(shù)即可。因此您會(huì)發(fā)現(xiàn)在本文中的例子中只需要這樣發(fā)送請(qǐng)求(參見(jiàn) 清單 10)。
清單 10. 發(fā)送請(qǐng)求
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.send(null);
}
|
指定回調(diào)方法
現(xiàn)在我們所做的只有很少一點(diǎn)是新的、革命性的或異步的。必須承認(rèn),open() 方法中 “true” 這個(gè)小小的關(guān)鍵字建立了異步請(qǐng)求。但是除此之外,這些代碼與用 Java servlet 及 JSP、PHP 或 Perl 編程沒(méi)有什么兩樣。那么 Ajax 和 Web 2.0 最大的秘密是什么呢?秘密就在于 XMLHttpRequest 的一個(gè)簡(jiǎn)單屬性 onreadystatechange。
首先一定要理解這些代碼中的流程(如果需要請(qǐng)回顧 清單 10)。建立其請(qǐng)求然后發(fā)出請(qǐng)求。此外,因?yàn)槭钱惒秸?qǐng)求,所以 JavaScript 方法(例子中的 getCustomerInfo())不會(huì)等待服務(wù)器。因此代碼將繼續(xù)執(zhí)行,就是說(shuō),將退出該方法而把控制返回給表單。用戶可以繼續(xù)輸入信息,應(yīng)用程序不會(huì)等待服務(wù)器。
這就提出了一個(gè)有趣的問(wèn)題:服務(wù)器完成了請(qǐng)求之后會(huì)發(fā)生什么?答案是什么也不發(fā)生,至少對(duì)現(xiàn)在的代碼而言如此!顯然這樣不行,因此服務(wù)器在完成通過(guò) XMLHttpRequest 發(fā)送給它的請(qǐng)求處理之后需要某種指示說(shuō)明怎么做。
|
在 JavaScript 中引用函數(shù)
JavaScript 是一種弱類型的語(yǔ)言,可以用變量引用任何東西。因此如果聲明了一個(gè)函數(shù) updatePage(),JavaScript 也將該函數(shù)名看作是一個(gè)變量。換句話說(shuō),可用變量名 updatePage 在代碼中引用函數(shù)。 |
|
現(xiàn)在 onreadystatechange 屬性該登場(chǎng)了。該屬性允許指定一個(gè)回調(diào)函數(shù)?;卣{(diào)允許服務(wù)器(猜得到嗎?)反向調(diào)用 Web 頁(yè)面中的代碼。它也給了服務(wù)器一定程度的控制權(quán),當(dāng)服務(wù)器完成請(qǐng)求之后,會(huì)查看 XMLHttpRequest 對(duì)象,特別是 onreadystatechange 屬性。然后調(diào)用該屬性指定的任何方法。之所以稱為回調(diào)是因?yàn)榉?wù)器向網(wǎng)頁(yè)發(fā)起調(diào)用,無(wú)論網(wǎng)頁(yè)本身在做什么。比方說(shuō),可能在用戶坐在椅子上手沒(méi)有碰鍵盤的時(shí)候調(diào)用該方法,但是也可能在用戶輸入、移動(dòng)鼠標(biāo)、滾動(dòng)屏幕或者點(diǎn)擊按鈕時(shí)調(diào)用該方法。它并不關(guān)心用戶在做什么。
這就是稱之為異步的原因:用戶在一層上操作表單,而在另一層上服務(wù)器響應(yīng)請(qǐng)求并觸發(fā) onreadystatechange 屬性指定的回調(diào)方法。因此需要像 清單 11 一樣在代碼中指定該方法。
清單 11. 設(shè)置回調(diào)方法
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
|
需要特別注意的是該屬性在代碼中設(shè)置的位置 —— 它是在調(diào)用 send() 之前 設(shè)置的。發(fā)送請(qǐng)求之前必須設(shè)置該屬性,這樣服務(wù)器在回答完成請(qǐng)求之后才能查看該屬性?,F(xiàn)在剩下的就只有編寫 updatePage() 方法了,這是本文最后一節(jié)要討論的重點(diǎn)。
處理服務(wù)器響應(yīng)
發(fā)送請(qǐng)求,用戶高興地使用 Web 表單(同時(shí)服務(wù)器在處理請(qǐng)求),而現(xiàn)在服務(wù)器完成了請(qǐng)求處理。服務(wù)器查看 onreadystatechange 屬性確定要調(diào)用的方法。除此以外,可以將您的應(yīng)用程序看作其他應(yīng)用程序一樣,無(wú)論是否異步。換句話說(shuō),不一定要采取特殊的動(dòng)作編寫響應(yīng)服務(wù)器的方法,只需要改變表單,讓用戶訪問(wèn)另一個(gè) URL 或者做響應(yīng)服務(wù)器需要的任何事情。這一節(jié)我們重點(diǎn)討論對(duì)服務(wù)器的響應(yīng)和一種典型的動(dòng)作 —— 即時(shí)改變用戶看到的表單中的一部分。
回調(diào)和 Ajax
現(xiàn)在我們已經(jīng)看到如何告訴服務(wù)器完成后應(yīng)該做什么:將 XMLHttpRequest 對(duì)象的 onreadystatechange 屬性設(shè)置為要運(yùn)行的函數(shù)名。這樣,當(dāng)服務(wù)器處理完請(qǐng)求后就會(huì)自動(dòng)調(diào)用該函數(shù)。也不需要擔(dān)心該函數(shù)的任何參數(shù)。我們從一個(gè)簡(jiǎn)單的方法開(kāi)始,如 清單 12 所示。
清單 12. 回調(diào)方法的代碼
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
function updatePage() {
alert("Server is done!");
}
</script>
|
它僅僅發(fā)出一些簡(jiǎn)單的警告,告訴您服務(wù)器什么時(shí)候完成了任務(wù)。在自己的網(wǎng)頁(yè)中試驗(yàn)這些代碼,然后在瀏覽器中打開(kāi)(如果希望查看該例中的 XHTML,請(qǐng)參閱 清單 8)。輸入電話號(hào)碼然后離開(kāi)該字段,將看到一個(gè)彈出的警告窗口(如 圖 3 所示),但是點(diǎn)擊 OK 又出現(xiàn)了……
圖 3. 彈出警告的 Ajax 代碼
根據(jù)瀏覽器的不同,在表單停止彈出警告之前會(huì)看到兩次、三次甚至四次警告。這是怎么回事呢?原來(lái)我們還沒(méi)有考慮 HTTP 就緒狀態(tài),這是請(qǐng)求/響應(yīng)循環(huán)中的一個(gè)重要部分。
HTTP 就緒狀態(tài)
前面提到,服務(wù)器在完成請(qǐng)求之后會(huì)在 XMLHttpRequest 的 onreadystatechange 屬性中查找要調(diào)用的方法。這是真的,但還不完整。事實(shí)上,每當(dāng) HTTP 就緒狀態(tài)改變時(shí)它都會(huì)調(diào)用該方法。這意味著什么呢?首先必須理解 HTTP 就緒狀態(tài)。
HTTP 就緒狀態(tài)表示請(qǐng)求的狀態(tài)或情形。它用于確定該請(qǐng)求是否已經(jīng)開(kāi)始、是否得到了響應(yīng)或者請(qǐng)求/響應(yīng)模型是否已經(jīng)完成。它還可以幫助確定讀取服務(wù)器提供的響應(yīng)文本或數(shù)據(jù)是否安全。在 Ajax 應(yīng)用程序中需要了解五種就緒狀態(tài):
- 0:請(qǐng)求沒(méi)有發(fā)出(在調(diào)用 open() 之前)。
- 1:請(qǐng)求已經(jīng)建立但還沒(méi)有發(fā)出(調(diào)用 send() 之前)。
- 2:請(qǐng)求已經(jīng)發(fā)出正在處理之中(這里通??梢詮捻憫?yīng)得到內(nèi)容頭部)。
- 3:請(qǐng)求已經(jīng)處理,響應(yīng)中通常有部分?jǐn)?shù)據(jù)可用,但是服務(wù)器還沒(méi)有完成響應(yīng)。
- 4:響應(yīng)已完成,可以訪問(wèn)服務(wù)器響應(yīng)并使用它。
與大多數(shù)跨瀏覽器問(wèn)題一樣,這些就緒狀態(tài)的使用也不盡一致。您也許期望任務(wù)就緒狀態(tài)從 0 到 1、2、3 再到 4,但實(shí)際上很少是這種情況。一些瀏覽器從不報(bào)告 0 或 1 而直接從 2 開(kāi)始,然后是 3 和 4。其他瀏覽器則報(bào)告所有的狀態(tài)。還有一些則多次報(bào)告就緒狀態(tài) 1。在上一節(jié)中看到,服務(wù)器多次調(diào)用 updatePage(),每次調(diào)用都會(huì)彈出警告框 —— 可能和預(yù)期的不同!
對(duì)于 Ajax 編程,需要直接處理的惟一狀態(tài)就是就緒狀態(tài) 4,它表示服務(wù)器響應(yīng)已經(jīng)完成,可以安全地使用響應(yīng)數(shù)據(jù)了?;诖?,回調(diào)方法中的第一行應(yīng)該如 清單 13 所示。
清單 13. 檢查就緒狀態(tài)
function updatePage() {
if (request.readyState == 4)
alert("Server is done!");
}
|
修改后就可以保證服務(wù)器的處理已經(jīng)完成。嘗試運(yùn)行新版本的 Ajax 代碼,現(xiàn)在就會(huì)看到與預(yù)期的一樣,只顯示一次警告信息了。
HTTP 狀態(tài)碼
雖然 清單 13 中的代碼看起來(lái)似乎不錯(cuò),但是還有一個(gè)問(wèn)題 —— 如果服務(wù)器響應(yīng)請(qǐng)求并完成了處理但是報(bào)告了一個(gè)錯(cuò)誤怎么辦?要知道,服務(wù)器端代碼應(yīng)該明白它是由 Ajax、JSP、普通 HTML 表單或其他類型的代碼調(diào)用的,但只能使用傳統(tǒng)的 Web 專用方法報(bào)告信息。而在 Web 世界中,HTTP 代碼可以處理請(qǐng)求中可能發(fā)生的各種問(wèn)題。
比方說(shuō),您肯定遇到過(guò)輸入了錯(cuò)誤的 URL 請(qǐng)求而得到 404 錯(cuò)誤碼的情形,它表示該頁(yè)面不存在。這僅僅是 HTTP 請(qǐng)求能夠收到的眾多錯(cuò)誤碼中的一種(完整的狀態(tài)碼列表請(qǐng)參閱 參考資料 中的鏈接)。表示所訪問(wèn)數(shù)據(jù)受到保護(hù)或者禁止訪問(wèn)的 403 和 401 也很常見(jiàn)。無(wú)論哪種情況,這些錯(cuò)誤碼都是從完成的響應(yīng) 得到的。換句話說(shuō),服務(wù)器履行了請(qǐng)求(即 HTTP 就緒狀態(tài)是 4)但是沒(méi)有返回客戶機(jī)預(yù)期的數(shù)據(jù)。
因此除了就緒狀態(tài)外,還需要檢查 HTTP 狀態(tài)。我們期望的狀態(tài)碼是 200,它表示一切順利。如果就緒狀態(tài)是 4 而且狀態(tài)碼是 200,就可以處理服務(wù)器的數(shù)據(jù)了,而且這些數(shù)據(jù)應(yīng)該就是要求的數(shù)據(jù)(而不是錯(cuò)誤或者其他有問(wèn)題的信息)。因此還要在回調(diào)方法中增加狀態(tài)檢查,如 清單 14 所示。
清單 14. 檢查 HTTP 狀態(tài)碼
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
}
|
為了增加更健壯的錯(cuò)誤處理并盡量避免過(guò)于復(fù)雜,可以增加一兩個(gè)狀態(tài)碼檢查,請(qǐng)看一看 清單 15 中修改后的 updatePage() 版本。
清單 15. 增加一點(diǎn)錯(cuò)誤檢查
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
else if (request.status == 404)
alert("Request URL does not exist");
else
alert("Error: status code is " + request.status);
}
|
現(xiàn)在將 getCustomerInfo() 中的 URL 改為不存在的 URL 看看會(huì)發(fā)生什么。應(yīng)該會(huì)看到警告信息說(shuō)明要求的 URL 不存在 —— 好極了!很難處理所有的錯(cuò)誤條件,但是這一小小的改變能夠涵蓋典型 Web 應(yīng)用程序中 80% 的問(wèn)題。
讀取響應(yīng)文本
現(xiàn)在可以確保請(qǐng)求已經(jīng)處理完成(通過(guò)就緒狀態(tài)),服務(wù)器給出了正常的響應(yīng)(通過(guò)狀態(tài)碼),最后我們可以處理服務(wù)器返回的數(shù)據(jù)了。返回的數(shù)據(jù)保存在 XMLHttpRequest 對(duì)象的 responseText 屬性中。
關(guān)于 responseText 中的文本內(nèi)容,比如格式和長(zhǎng)度,有意保持含糊。這樣服務(wù)器就可以將文本設(shè)置成任何內(nèi)容。比方說(shuō),一種腳本可能返回逗號(hào)分隔的值,另一種則使用管道符(即 | 字符)分隔的值,還有一種則返回長(zhǎng)文本字符串。何去何從由服務(wù)器決定。
在本文使用的例子中,服務(wù)器返回客戶的上一個(gè)訂單和客戶地址,中間用管道符分開(kāi)。然后使用訂單和地址設(shè)置表單中的元素值,清單 16 給出了更新顯示內(nèi)容的代碼。
清單 16. 處理服務(wù)器響應(yīng)
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, " ");
} else
alert("status is " + request.status);
}
}
|
首先,得到 responseText 并使用 JavaScript split() 方法從管道符分開(kāi)。得到的數(shù)組放到 response 中。數(shù)組中的第一個(gè)值 —— 上一個(gè)訂單 —— 用 response[0] 訪問(wèn),被設(shè)置為 ID 為 “order” 的字段的值。第二個(gè)值 response[1],即客戶地址,則需要更多一點(diǎn)處理。因?yàn)榈刂分械男杏靡话愕男蟹指舴ā癨n”字符)分隔,代碼中需要用 XHTML 風(fēng)格的行分隔符 <br /> 來(lái)代替。替換過(guò)程使用 replace() 函數(shù)和正則表達(dá)式完成。最后,修改后的文本作為 HTML 表單 div 中的內(nèi)部 HTML。結(jié)果就是表單突然用客戶信息更新了,如圖 4 所示。
圖 4. 收到客戶數(shù)據(jù)后的 Break Neck 表單
結(jié)束本文之前,我還要介紹 XMLHttpRequest 的另一個(gè)重要屬性 responseXML。如果服務(wù)器選擇使用 XML 響應(yīng)則該屬性包含(也許您已經(jīng)猜到)XML 響應(yīng)。處理 XML 響應(yīng)和處理普通文本有很大不同,涉及到解析、文檔對(duì)象模型(DOM)和其他一些問(wèn)題。后面的文章中將進(jìn)一步介紹 XML。但是因?yàn)?responseXML 通常和 responseText 一起討論,這里有必要提一提。對(duì)于很多簡(jiǎn)單的 Ajax 應(yīng)用程序 responseText 就夠了,但是您很快就會(huì)看到通過(guò) Ajax 應(yīng)用程序也能很好地處理 XML。
結(jié)束語(yǔ)
您可能對(duì) XMLHttpRequest 感到有點(diǎn)厭倦了,我很少看到一整篇文章討論一個(gè)對(duì)象,特別是這種簡(jiǎn)單的對(duì)象。但是您將在使用 Ajax 編寫的每個(gè)頁(yè)面和應(yīng)用程序中反復(fù)使用該對(duì)象。坦白地說(shuō),關(guān)于 XMLHttpRequest 還真有一些可說(shuō)的內(nèi)容。下一期文章中將介紹如何在請(qǐng)求中使用 POST 及 GET,來(lái)設(shè)置請(qǐng)求中的內(nèi)容頭部和從服務(wù)器響應(yīng)讀取內(nèi)容頭部,理解如何在請(qǐng)求/響應(yīng)模型中編碼請(qǐng)求和處理 XML。
再往后我們將介紹常見(jiàn) Ajax 工具箱。這些工具箱實(shí)際上隱藏了本文所述的很多細(xì)節(jié),使得 Ajax 編程更容易。您也許會(huì)想,既然有這么多工具箱為何還要對(duì)底層的細(xì)節(jié)編碼。答案是,如果不知道應(yīng)用程序在做什么,就很難發(fā)現(xiàn)應(yīng)用程序中的問(wèn)題。
因此不要忽略這些細(xì)節(jié)或者簡(jiǎn)單地瀏覽一下,如果便捷華麗的工具箱出現(xiàn)了錯(cuò)誤,您就不必?fù)项^或者發(fā)送郵件請(qǐng)求支持了。如果了解如何直接使用 XMLHttpRequest,就會(huì)發(fā)現(xiàn)很容易調(diào)試和解決最奇怪的問(wèn)題。只有讓其解決您的問(wèn)題,工具箱才是好東西。
因此請(qǐng)熟悉 XMLHttpRequest 吧。事實(shí)上,如果您有使用工具箱的 Ajax 代碼,可以嘗試使用 XMLHttpRequest 對(duì)象及其屬性和方法重新改寫。這是一種不錯(cuò)的練習(xí),可以幫助您更好地理解其中的原理。
JFace中的工具條和菜單
前一節(jié)中我們簡(jiǎn)單介紹了一下
Action
。其實(shí)所謂的
Action
就是一個(gè)最常用的事件,舉個(gè)例子來(lái)說(shuō),對(duì)于一個(gè)按鈕來(lái)說(shuō)它可以有多個(gè)事件,比如按鍵,焦點(diǎn),鼠標(biāo),等等等等吧,但是實(shí)際上在使用程序的時(shí)候,我們最關(guān)心的,就是按下去這個(gè)按鈕會(huì)發(fā)生什么,這個(gè)其實(shí)就是所謂的
Action
。如果大家以前做過(guò)
swing/awt
變成的話,應(yīng)該對(duì)
Action
不會(huì)陌生。
在
JFace
里面,一個(gè)
Action
可以對(duì)應(yīng)多個(gè)
GUI
對(duì)象,這些對(duì)象就是所謂的
Contribution Item
。比如我們?cè)谝话愠绦蚶锩婧艹R?jiàn)的“文件”菜單,下面都會(huì)有“新建”,“保存”等等。同時(shí)我們可以在工具條上放置相應(yīng)的按鈕,那么這些都是有相同的功能,在
JFace
里面我們可以只寫一個(gè)
Action
,然后把它映射到不同的
ContributionItem
去,而不必為每個(gè)部件都寫一串處理事件。
我們下面還是通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明,在
JFace
中怎么使用菜單和工具條這兩種最基本也是最有用的
Contribution Item
。
我們這個(gè)程序?qū)懙煤苌?,就是一個(gè)光禿禿的窗口上做了一個(gè)菜單和工具條按鈕,功能也只有一個(gè),就是每次點(diǎn)一下,就彈出一個(gè)輸入框來(lái)問(wèn)你名字是什么,然后顯示一個(gè)
Hello, xxx
之類的。
首先我們還是來(lái)寫一個(gè)
Action
類:
?1?
?2?public?class?SayHiAction?extends?Action?{
?3?????private?Shell?shell;
?4?
?5?????public?SayHiAction(Shell?shell)?{
?6?????????super();
?7?????????this.shell?=?shell;
?8?????????this.setText("Say&Hi@Ctrl+H");
?9?????}
10?
11?????@Override
12?????public?void?run()?{
13?????????InputDialog?input?=?new?InputDialog(shell,?"Input?your?name",
14?????????????????"Please?input?your?name?here:",?null,?null);
15?????????if?(input.open()?==?Window.OK)?{
16?????????????MessageDialog.openInformation(shell,?"Hello",?"Hello,?"
17?????????????????????+?input.getValue()?+?"!");
18?????????}
19?
20?????}
21?
22?}
23?
代碼段
17
這只是一個(gè)很簡(jiǎn)單的
Action
類,沒(méi)有太多可說(shuō)的。
然后我們創(chuàng)建一個(gè)
ApplicationWindow
類:
?1?
?2?public?class?Hiyou?extends?ApplicationWindow?{
?3?
?4?????private?SayHiAction?hiaction;
?5?????public?Hiyou(Shell?parentShell)?{
?6?????????super(parentShell);
?7?????????hiaction=new?SayHiAction(getShell());
?8?????????addMenuBar();
?9?????????addToolBar(SWT.FLAT?|?SWT.WRAP);
10?????}
11?
12?????@Override
13?????protected?ToolBarManager?createToolBarManager(int?style)?{
14?????????ToolBarManager?toolbar=new?ToolBarManager();
15?????????toolbar.add(hiaction);
16?????????return?toolbar;
17?????}
18?
19?????@Override
20?????protected?MenuManager?createMenuManager()?{
21?????????MenuManager?menubar=new?MenuManager();
22?????????MenuManager?fileMenu=new?MenuManager("&File");
23?????????fileMenu.add(hiaction);
24?????????menubar.add(fileMenu);
25?????????return?menubar;
26?????}
27?
28?????/**
29??????*?@param?args
30??????*/
31?????public?static?void?main(String[]?args)?{
32?????????Hiyou?window=new?Hiyou(null);
33?????????window.setBlockOnOpen(true);
34?????????window.open();
35?????????Display.getCurrent().dispose();
36?????}
37?
38?}
39?
?
代碼段
18
大家可能已經(jīng)注意到了,在這里面我們重載了
createMenuManager
和
createToolBarManager
兩個(gè)方法,它們的用途就和名字一樣,一個(gè)是用來(lái)創(chuàng)建菜單的,一個(gè)是用來(lái)創(chuàng)建工具條的。重載了這兩個(gè)方法以后,通過(guò)在構(gòu)造函數(shù)中調(diào)用
addMenuBar
和
addToolBar
讓工具條和菜單顯示出來(lái)。
這里值得一提的是
MenuManager
和
ToolBarManager
類,如果大家翻一下
API
文檔的話會(huì)發(fā)現(xiàn)它們都是所謂的
contribution manager
(實(shí)現(xiàn)了
IConntributionManager
接口),你可以通過(guò)這些
contribution manager
來(lái)實(shí)現(xiàn)對(duì)特定組件的管理(添加刪除等等)。
具體到菜單的創(chuàng)建,看了我們上面的代碼就很明白了,就直接調(diào)用相應(yīng)
MenuManager
的
add
方法把
action
添加上就可以了。
JFace
會(huì)自動(dòng)找到這個(gè)
Action
的
getText
方法設(shè)置菜單的文字。如果是有好幾層菜單,那么只要在重新
new
一個(gè)
MenuManager
添加到已有的
MenuManager
里面就可以了。就象前面代碼中的:
menubar.add(fileMenu);
至于工具條就更簡(jiǎn)單了,創(chuàng)建一個(gè)
ToolBarManager
然后直接
add
對(duì)應(yīng)的
Action
就可以了。
添加圖標(biāo)
如果菜單只是文字還沒(méi)有什么,如果你的工具條都是文字是不是會(huì)顯得干巴巴的?其實(shí)只要我們?yōu)?/span>
Action
設(shè)置
ImageDescriptor
就可以了,比如你可以自己畫一個(gè)圖標(biāo)保存到
Action
的包下面(我畫了一個(gè)
hi.gif
),然后把
Action
的構(gòu)造函數(shù)改寫成這樣:
public
?SayHiAction(Shell?shell)?{
????????
super
();
????????
this
.shell?
=
?shell;
????????
this
.setText(
"
Say&Hi@Ctrl+H
"
);
????????
this
.setImageDescriptor(ImageDescriptor.createFromFile(
this
.getClass(),
????????????????
"
hi.gif
"
));
????}
大家注意最后一句話,就是為
action
設(shè)置圖標(biāo)的。然后再運(yùn)行一下就會(huì)發(fā)現(xiàn)菜單和工具欄都有圖標(biāo)了。
JFace中的事件模式
大家好,因?yàn)楣ぷ鞯氖虑楦懔艘粋€(gè)多月,現(xiàn)在終于暫時(shí)安定下來(lái)了。這一系列的文章我也會(huì)繼續(xù)往下寫。
在這一節(jié)中,我會(huì)向大家介紹
JFace
中的事件模式。其實(shí)我相信這篇文章的讀者應(yīng)該大部分都會(huì)接觸
eclipse
,這樣可能也會(huì)接觸過(guò)
eclipse
的插件開(kāi)發(fā)。就是沒(méi)有接觸過(guò),大家也可能會(huì)有在
eclipse
里面新建工程的時(shí)候出于各種原因(比如好奇心)點(diǎn)了
plug-in project
的時(shí)候吧。其實(shí)作為一個(gè)程序員來(lái)講,保持好奇是很重要的。如果你大概看過(guò)一個(gè)
plug-in project
的結(jié)構(gòu),雖然可能不能全部理解,但是我相信也應(yīng)該對(duì)
Action
之類有一些了解。我們這一節(jié)主要就是圍繞
Action
來(lái)寫的。為了增加可讀性,我們首先介紹幾個(gè)名詞,這些名詞都可以從
eclipse
的文檔中找到。
什么是
Action
JFace
中的一個(gè)
Action
可以簡(jiǎn)單地理解成一個(gè)命令。那么它和事件有什么關(guān)系呢?比如說(shuō)我點(diǎn)了一個(gè)菜單,那么點(diǎn)擊本身就是一個(gè)事件,但是這個(gè)事件的影響就是相應(yīng)的命令被執(zhí)行了。大家日常使用的一些軟件比如
Office
都是有菜單和工具欄的,而一個(gè)菜單項(xiàng)和一個(gè)工具欄可能執(zhí)行的是同一個(gè)命令。比如
Word
里面要新建一個(gè)文檔的話可以通過(guò)
“
文件
”
菜單下的
“
新建
”
實(shí)現(xiàn),也可以通過(guò)點(diǎn)擊工具欄上相應(yīng)的圖標(biāo)實(shí)現(xiàn)。這個(gè)新建地功能本身在
JFace
里面是可以使用
Action
來(lái)實(shí)現(xiàn)的。
在
JFace
里面,
Action
可以關(guān)聯(lián)到菜單,工具條,以及按鈕(也就是
Button
)。當(dāng)然關(guān)于如何關(guān)聯(lián),我們會(huì)在后面向大家詳細(xì)介紹。
Action
在
JFace
里面的定義是一個(gè)接口
org.eclipse.jface.action.IAction
。當(dāng)然實(shí)際上你寫程序的時(shí)候必須自己來(lái)實(shí)現(xiàn)這個(gè)接口,寫出自己的
Action
類來(lái)。
IAction
里面最重要的方法是
run()
,它也是事件觸發(fā)以后執(zhí)行的代碼。其他的方法都是一些輔助性的方法,不是我們要關(guān)注的重點(diǎn)。為了能夠?qū)⒕性谖覀兯P(guān)注的事情上,通常我們不是實(shí)現(xiàn)
IAction
接口,而是通過(guò)繼承
org.eclipse.jface.action.Action
這個(gè)抽象類來(lái)實(shí)現(xiàn)
Action
。下面我們通過(guò)一個(gè)例子來(lái)說(shuō)明
Action
的用法。
Hello,Action!
首先我們先不管用戶界面,先定義一個(gè)最簡(jiǎn)單的
Action
類。
?1?public?class?HelloAction?extends?Action{
?2?????private?Shell?shell;
?3?????
?4?????public?HelloAction(Shell?shell)?{????
?5?????????super("&Hello",Action.AS_PUSH_BUTTON);
?6?????????this.shell=shell;
?7?????}
?8?
?9?
10?????@Override
11?????public?void?run()?{
12?????????MessageDialog.openInformation(shell,?"Hello",?"Hello,Action!");
13?????}
14?????
15?}
16?
代碼段
1
5
這段代碼其實(shí)應(yīng)該還是很好讀懂的。帶參的構(gòu)造函數(shù)帶進(jìn)來(lái)一個(gè)
Shell
實(shí)例,而
run()
方法說(shuō)明了這個(gè)
Action
的功能就是顯示一個(gè)對(duì)話框。第
5
行中的代碼調(diào)用了父類的構(gòu)造函數(shù),其中第一個(gè)參數(shù)是
Action
對(duì)應(yīng)的文本,前面的
&
符號(hào)表明了
H
是熱鍵,而第二個(gè)參數(shù)則是一個(gè)風(fēng)格參數(shù)。如果大家繼續(xù)向后看的話,就會(huì)發(fā)現(xiàn)這個(gè)
Action
被附加在了一個(gè)按鈕上面,而按鈕上顯示的文本就是
Hello
,如果你定義的風(fēng)格不是
AS_PUSH_BUTTON
而是
AS_RADIO_BUTTON
的話就會(huì)發(fā)現(xiàn)按鈕已經(jīng)不是一個(gè)純粹的按鈕了,而是一個(gè)單選鈕。相應(yīng)的其他風(fēng)格可以參照
Javadoc。
?1?
?2?public?class?HelloJface?extends?ApplicationWindow?{??????
?3?????public?HelloJface(Shell?shell)?{
?4?????????super(shell);
?5?????}
?6???????@Override
?7?????protected?Control?createContents(Composite?parent)?{
?8???????????HelloAction?action=new?HelloAction(parent.getShell());
?9???????????ActionContributionItem?aci=new?ActionContributionItem(action);
10???????????aci.fill(parent);
11???????????return?parent;
12????}??
13????/**
14?????*?@param?args
15?????*/
16????public?static?void?main(String[]?args)?{
17?
18????????HelloJface?demo?=?new?HelloJface(null);
19????????demo.setBlockOnOpen(true);
20????????demo.open();
21????????Display.getCurrent().dispose();
22?
23????}
24?}
代碼段
16
和前面一節(jié)的代碼相比,我們只是修改了
createContents
方法。首先創(chuàng)建了一個(gè)
HelloAction
的實(shí)例,然后又創(chuàng)建了一個(gè)
ActionContributionItem
的實(shí)例,最后調(diào)用了這個(gè)實(shí)例的
fill
方法將按鈕添加到窗口中,這就是全部了。是不是很簡(jiǎn)單呢?程序運(yùn)行出來(lái)的效果如下圖:
圖
1
6
可能看了這個(gè)例子,你會(huì)認(rèn)為
ActionContributionItem
這個(gè)類表示的就是一個(gè)按鈕了。但是實(shí)際上并不是的,它在圖形界面上表示成什么樣子,隨著不同的
fill
調(diào)用又有不同。在下一節(jié)中,我會(huì)向大家深入介紹
Contribution Item
以及
JFace
中的菜單,工具條等的應(yīng)用。這一節(jié)就到這里結(jié)束了,因?yàn)閯倓倱Q了工作環(huán)境,有很多事情需要去做,所以寫得比較短,請(qǐng)大家見(jiàn)諒:)。
摘要: JFace
的
Hello,world
!
我們?nèi)匀皇菑囊粋€(gè)最簡(jiǎn)單的
Hello,world!
開(kāi)始介紹
JFace
。為了更形象一些,首先把程序列...
閱讀全文
JFace
以及其他
關(guān)于
JFace
:一個(gè)簡(jiǎn)單的介紹
我們已經(jīng)有了
swt
,我們用
swt
可以寫出一個(gè)完整的程序來(lái),那么我們?yōu)槭裁葱枰?/span>
Jface
呢?
對(duì)于這一點(diǎn),本文作者(就是我了,嘿嘿)的理解是:使用
JFace
比只是單純地使用
swt
編程更加簡(jiǎn)單,或者說(shuō):代碼量更少。畢竟,你完全可以用匯編寫一個(gè)用戶界面,但是付出的代價(jià)似乎大了一點(diǎn)
:P
。
如果你在使用
swt
編程,那么
JFace
的知識(shí)并不是必需的:你完全可以不用
JFace
就可以寫出任何你需要的功能。但是如果你使用
JFace
,你必需對(duì)
swt
有一些了解,因?yàn)?/span>
JFace
需要
swt
的各種部件構(gòu)建用戶界面。
我覺(jué)得我們可以在某種程度上這樣看
JFace
:它封裝了一部分
swt
的功能,所謂“封裝”可以從幾個(gè)方面來(lái)看:
首先,你可以使用
JFace
的某些機(jī)制來(lái)代替
swt
中的一些機(jī)制
其次,
JFace
中各種功能的實(shí)現(xiàn)都是依賴于底層的
swt
的。
最后,你可以在使用
JFace
時(shí)候同時(shí)使用
swt
。
這篇文章的組織結(jié)構(gòu)
在這篇文章以后的部分,我將會(huì)向大家介紹以下的內(nèi)容:
首先,我會(huì)從一個(gè)簡(jiǎn)單的示例程序開(kāi)始展示如何開(kāi)始寫一個(gè)
JFace
程序
之后我會(huì)向大家介紹
JFace
的事件模型(與
swt
的事件模型不同)
然后我會(huì)向大家介紹與構(gòu)建
JFace
用戶界面相關(guān)的一些知識(shí)。
目前來(lái)講,因?yàn)槲覄倓倢懙竭@里,這是我所能想到的一些部分,當(dāng)然,可能在以后的文章中略有不同。
另外的參考資料
在這一系列文章的第一節(jié)(
http://blog.csdn.net/jayliu/archive/2005/04/29/367757.aspx
)中,我向大家介紹了一些參考資料。現(xiàn)在向大家再介紹一篇在
IBM developerworks
上發(fā)現(xiàn)的一篇很好的文章:
在
eclipse
Workbench
之外使用
eclipse
GUI
,這篇文章共有三部分,地址列在下面:
http://www-128.ibm.com/developerworks/cn/linux/opensource/os-ecgui1/index.html
http://www-128.ibm.com/developerworks/cn/linux/opensource/os-ecgui2/index.html
http://www-128.ibm.com/developerworks/cn/linux/opensource/os-ecgui3/index.html
環(huán)境的配置
關(guān)于如何配置編程環(huán)境,可以參照這一系列文章的第一篇(
http://blog.csdn.net/jayliu/archive/2005/04/29/367757.aspx
),在這里我不再贅述。
摘要: 幾種特殊的部件
好像因?yàn)榇蠹业谋г贡容^多,感覺(jué)這一段
csdn
的
blog
似乎又有恢復(fù)穩(wěn)定的跡象了,
^_^
。
...
閱讀全文
使用
Layout
管理
UI
布局
我們?cè)谇懊嬖谝恍├又幸呀?jīng)使用過(guò)
Layout
了。那么
Layout
到底是做什么的呢?
我們知道,在設(shè)計(jì)用戶界面時(shí)候,我們可以采用的一種辦法是手動(dòng)的為每個(gè)部件設(shè)置合適當(dāng)大小和位置。但是這樣的話,如果你所要顯示的部件比較多,編程量就會(huì)非常大,特別是考慮到窗體大小變化時(shí)候各種部件的重繪。而實(shí)際上,我們可以利用一些通用的規(guī)則或者說(shuō)算法來(lái)安排這些部件的排列。比如下圖所示的這個(gè)窗體:
圖
10
很顯然,對(duì)于這些規(guī)則化的部件排列,應(yīng)該有一些更簡(jiǎn)單的辦法,而不是在代碼的各個(gè)部分寫
xxx.setBounds
之類。
swt
給我們提供了這樣的辦法,就是使用
Layout
。
你可以把一個(gè)
Layout
看成是安排部件位置和大小的一個(gè)規(guī)則,在應(yīng)用了
Layout
的
Composite
(我們?cè)谶@里第一次提到了
Composite
,
Composite
就是一個(gè)能夠包含其他控件的容器,比如
Shell
就是一個(gè)
Composite
,我們會(huì)在后面的部分詳細(xì)介紹
Composite
)中,所有的子控件都會(huì)按照這個(gè)規(guī)則來(lái)進(jìn)行排列。
在目前(寫這篇文章的時(shí)候
eclipse
最新版本是
3.1M7
),
org.eclipse.swt.layout
包中包含四種已經(jīng)定義好的
Layout
,它們分別是:
FillLayout
,
FormLayou
,
GridLayout
和
RowLayout
。我在這里不再一一作介紹,而是推薦大家看一下這篇文章《
Understanding Layouts in SWT
》。相信如果你有興趣看完它的話,就會(huì)對(duì)
Layout
有比較深的了解。
使用
Visual Editor
加速你的開(kāi)發(fā)
在
eclipse
中,你可以使用
Visual Editor
來(lái)進(jìn)行拖放式的圖形界面設(shè)計(jì)。實(shí)際上我覺(jué)得在這一方面大家應(yīng)該可以很容易上手。
如果你安裝了
VE
的話,在新建一個(gè)
swt
工程的時(shí)候你就可以省好多力氣了:你不需要找那些名字
n
長(zhǎng)的
jar
,然后一個(gè)一個(gè)導(dǎo)入工程的
build path
里面,而是僅僅需要添加一個(gè)
User Library
,就像下面圖示一樣:
圖
7
你可以新建一個(gè)
Visual Class
。
圖8
之后就可以使用
VE
進(jìn)行可視化的編輯了:
圖
9
關(guān)于
VE
的具體應(yīng)用,我在這里不作具體介紹(說(shuō)實(shí)話,我覺(jué)得用起來(lái)還是比較簡(jiǎn)單的),如果你覺(jué)得實(shí)在有必要看一篇詳細(xì)的指南的話,可以參照這篇文章:
Extending The Visual Editor
Tutorial: Enabling support for a custom widget
摘要: 讓你的
swt
程序動(dòng)起來(lái)
在向使用者提供最差的用戶體驗(yàn)方面,中國(guó)的
IT
企業(yè)始終走在時(shí)代的最前端。之所以有這樣的感慨其實(shí)是來(lái)源于往
blog
上貼...
閱讀全文
摘要: 寫在前面的話
終于決定提起筆來(lái)寫一篇關(guān)于
swt
和
JFace
編程的文章。在開(kāi)始之前,我想先介紹一下你能夠從將要出現(xiàn)的這一系列文章里得到什么,以及更重要的,你不能得到什么。我們的時(shí)間是如此之重要,以至于我們很難容忍...
閱讀全文