Java Votary

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            48 隨筆 :: 1 文章 :: 80 評(píng)論 :: 0 Trackbacks

          #

          這里說說我的經(jīng)歷吧。大學(xué)前以及大學(xué)前面三年的經(jīng)歷就不說了,因?yàn)榇髮W(xué)前的高中就是好好學(xué)習(xí),大學(xué)前三年就是混過來的。

              我上的學(xué)校還算可以,雖然不是北大清華這樣第一流名牌大學(xué),但至少也算中國(guó)的第二流名牌大學(xué)了。大學(xué)中前面三年都陪伴著游戲過去,所學(xué)到的只是些計(jì)算機(jī)基 礎(chǔ)知識(shí)。到大四后我突然發(fā)現(xiàn)就業(yè)的問題就在眼前,而自己似乎什么也不會(huì),于是開始看書。最一開始重點(diǎn)看的是C++,可是后來自從看了一本J2ME的書以后 被Java所吸引。當(dāng)時(shí)雖然學(xué)校上過Java課程,但是自己也只是學(xué)了很少的皮毛,也就只會(huì)寫寫Hello World和什么加減法之類很簡(jiǎn)單的程序,連API都知道沒有幾個(gè),比如說字符串長(zhǎng)度的API我都不知道。所以剛開始自己學(xué)J2ME的時(shí)候?qū)覍沂艽欤约? 也明白自己的缺點(diǎn),決定從J2SE開始好好補(bǔ)上。

              剛開始為了熟悉Java開發(fā)環(huán)境,買了本JBuilder開發(fā)的教程,并且在自己的本本上安裝了JBuilder進(jìn)行演練。當(dāng)時(shí)的我連JavaDoc都不 知道,每次究竟什么API能做什么事情一點(diǎn)頭緒都沒有,還不知道哪里去查,后來同學(xué)告訴我有個(gè)JavaDoc這個(gè)東西,我還興奮不已,覺得自己被從黑暗中 拉回來了。一開始使用JBuilder的時(shí)候,馬上為之所吸引,有兩個(gè)原因,第一是因?yàn)樗詣?dòng)標(biāo)出語(yǔ)法錯(cuò)誤,邊寫代碼邊提示你什么地方語(yǔ)法出錯(cuò),記得以前 使用VC++的時(shí)候,每次程序?qū)懞煤笙染幾g,然后再Build,再運(yùn)行,這其中每個(gè)步驟都會(huì)出不少錯(cuò)誤。特別是在編譯的時(shí)候,寫個(gè)200多行的程序一次編 譯下來就有100多個(gè)錯(cuò)誤,結(jié)果每次花在這上面的工夫都要好長(zhǎng)時(shí)間。而JBuilder使用了即時(shí)語(yǔ)法分析,所以基本上程序?qū)懲辏涂梢允÷哉{(diào)試語(yǔ)法錯(cuò)誤 的步驟了。第二個(gè)原因是可以自動(dòng)提示代碼,這個(gè)功能可以讓你迅速熟悉API,免得每次去查幫助文檔那么麻煩,我就是這么很快掌握了許多API的。

          可能大家會(huì)問我為什么一開始不學(xué)習(xí)《Java編程思想》,的確這本書我們宿舍就有好幾本,不過大家普遍反映效果不好,到最后都不知道說的是什么,所以我也沒敢看。

              經(jīng)過20天左右的學(xué)習(xí),對(duì)Java有了更進(jìn)一步的了解,熟悉了不少API函數(shù),由于在那本書上寫開發(fā)SWING占了不少篇幅,所以也對(duì)Swing的開發(fā)了 解了不少。看完以后因?yàn)橥瑢W(xué)說Java的靈魂就是多線程編程,所以開始看Oreilly的《Java線程》。記得在大學(xué)中操作系統(tǒng)這門課我們就提到過線程 的知識(shí)。并且課本上就是用Java實(shí)現(xiàn)的,當(dāng)時(shí)有了一點(diǎn)點(diǎn)概念,但這次看這本專門說線程的書后才發(fā)現(xiàn)我原來了解的那些根本是什么都不算(當(dāng)然,現(xiàn)在回想起 來,我那時(shí)看書學(xué)到的也只是很簡(jiǎn)單的皮毛而已)。看完這本書后我自己學(xué)會(huì)在我的JBuilder下開發(fā)很簡(jiǎn)單的多線程程序,并且模擬線程沖突,等待等情 況。當(dāng)時(shí)看著自己寫的一兩百行程序可以順利執(zhí)行,那種興奮勁就別提了。這本書我看得也很快,大概就花了3個(gè)星期看完。

              經(jīng)過上面的學(xué)習(xí),自己相比以前來說提升了不少,這時(shí)候自己也找到了工作,是做J2EE對(duì)日外包的,所以更加堅(jiān)定了努力學(xué)習(xí)Java的信心。

              在上面寫的程序中,我自己寫程序沒有規(guī)范性,在代碼編寫的時(shí)候自己的盲點(diǎn)特別多,還容易犯低級(jí)失誤。同學(xué)有一個(gè)《Effective Java》中文版,可是我看了幾頁(yè)發(fā)現(xiàn)自己根本看不懂,里面什么靜態(tài)工廠啊,什么單例模式什么的根本不知道什么東東。我知道自己目前的水平還不夠,所以決 定放下這本書,去尋找別的適合我的書看。這個(gè)時(shí)候我看到了候捷先生翻譯的《Practical Java》一書,當(dāng)時(shí)是剛剛上的書架。這本書我在書店翻了下目錄后就感覺如獲至寶,馬上買回家,在回家的公車上就貪婪地讀起來。這本書不算很厚,但是自己 看得卻很認(rèn)真很仔細(xì),也明白了不少東西,比如Java中等號(hào)和equals()方法的區(qū)別,究竟什么時(shí)候用什么。還有Exception處理機(jī)制,以前不 知道什么叫Exception,只是JBuilder提示我要我拋出Exception我再拋出Exception,自己覺得這東西基本沒什么用呢。但是 看了這本書后我改變了看法,我發(fā)現(xiàn)Exception是個(gè)很好的東西,可以迅速把程序從正常狀態(tài)和異常狀態(tài)區(qū)分開來,即使而準(zhǔn)確地在指定位置得到處理。那 時(shí)自己也有了以后寫程序的時(shí)候注意編寫異常處理部分的想法。《Practical Java》這本書雖然不厚,但是我卻非常仔細(xì)地去看了,大概花了1個(gè)月時(shí)間,我把這本書完全消化了下去。

              當(dāng)時(shí)聽說Java在網(wǎng)絡(luò)上的應(yīng)用非常廣,我也不知道究竟是什么應(yīng)用,我于是買了Oreilly的《Java網(wǎng)絡(luò)編程》這本書。這本書雖然很厚,其實(shí)前半部 分內(nèi)容不是很復(fù)雜,后半部分寫什么RMI的東西我也看不大懂,只能理解個(gè)概念。通過這本書,我了解了HTTP協(xié)議究竟是什么一個(gè)東西,在它上面利用 Java傳輸數(shù)據(jù)該如何做,知道了什么是Request,什么是Response。這也為以后開始我的J2EE之旅打下了很好的基礎(chǔ)。當(dāng)時(shí)自己依然是邊看 書邊自己寫代碼來驗(yàn)證,自己寫了個(gè)服務(wù)器端Socket和客戶端Socket,成功進(jìn)行了通信,又在上面加上了安全Socket內(nèi)容,實(shí)現(xiàn)了SSL通信。 當(dāng)時(shí)我把寫的這個(gè)又套上了Swing的外殼,還和同學(xué)拿這個(gè)傳文件呢。不過當(dāng)時(shí)也沒有考慮過什么校驗(yàn)碼之類的東西,所以傳傳小文件還是可以的,文件稍微一 大一點(diǎn),傳過去的文件總是不對(duì)頭,和我原來的文件經(jīng)常會(huì)出一些差異,導(dǎo)致文件打不開。

              《Java網(wǎng)絡(luò)編程》這本書看了不少時(shí)間,因?yàn)闀容^厚,東西也比較多,不過除了后面的一些知識(shí)以外,其他的還是容易理解的。大概花了2個(gè)月左右的時(shí)間看 完。看完后,時(shí)間也到了2004年的3月。我也輪到開始我畢業(yè)設(shè)計(jì)的時(shí)候了。我們的畢業(yè)設(shè)計(jì)導(dǎo)師都還不錯(cuò),給你自己選個(gè)課題,我選的是一個(gè)B/S結(jié)構(gòu)的在 線簡(jiǎn)歷處理系統(tǒng),正好和我所學(xué)和下面所工作的東西是一條路上的了。這時(shí)我覺得我應(yīng)該往B/S結(jié)構(gòu)上轉(zhuǎn)了,當(dāng)時(shí)在選擇先看Servlet還是先看JSP上猶 豫不決。最終決定先看Servlet,后來也證明了我的決定是對(duì)的,我在熟悉了Servlet后再學(xué)JSP是非常容易的,基本上根本沒有遇到什么難點(diǎn)。

          可 能有人會(huì)覺得我看了好多Oreilly的書,雖然我不能說Oreilly本本都是好書,不過相對(duì)來說,好書的概率總超過許多其他的出版社,而且體系比較齊 全。我看得幾本書我都覺得還不錯(cuò)。現(xiàn)說說下面這本我學(xué)Servlet時(shí)候看的《Java Servlet編程》來說吧,很不錯(cuò)的一本書,讓我迅速知道了什么是Servlet,然后通過最簡(jiǎn)單的實(shí)例,讓你知道了Servlet如何運(yùn)行的,跟 HTTP協(xié)議是如何配合的,如何返回HTML形式的文本,XML配置符該如何寫,究竟每個(gè)元素是什么意思等等。由于我原來有一定的XML基礎(chǔ)(知道XML 語(yǔ)法各種格式的含義而已),所以掌握起來還算比較快。通過這本書,我知道了如何動(dòng)態(tài)生成HTML文檔,知道如何把一個(gè)Servlet映射到一個(gè)虛擬的地 址。在后半部分寫到了數(shù)據(jù)庫(kù)操作部分,我對(duì)數(shù)據(jù)庫(kù)的了解其實(shí)也僅限于原來大學(xué)課本上的《數(shù)據(jù)庫(kù)系統(tǒng)原理》,如何從程序和數(shù)據(jù)庫(kù)交互是一竅不通。通過數(shù)據(jù)庫(kù) 操作這章,我知道了如何使用JDBC語(yǔ)句如何編寫,大家不要笑,對(duì)于當(dāng)初一個(gè)新手來說,這個(gè)真是一個(gè)全新的領(lǐng)域,做什么事情都需要Sample來對(duì)照,跟 著依葫蘆畫瓢吧,其實(shí)現(xiàn)在的軟件開發(fā)也是這樣,我想現(xiàn)在大家誰(shuí)能直接手寫Struts或者Hibernate的配置文件都很難吧。閑話少說,大概這個(gè)時(shí) 候,我對(duì)畢業(yè)設(shè)計(jì)的雛形有了點(diǎn)思想上的概念。看完了《Java Servlet編程》后緊接著就又看Oreilly的《JSP設(shè)計(jì)》,由于有了Servlet的基礎(chǔ),學(xué)起JSP特別快。當(dāng)時(shí)沒有著重看Tag的自定義設(shè) 計(jì),光看了JSP的其他東西,終于在五一節(jié)后把畢業(yè)設(shè)計(jì)都寫完了,當(dāng)時(shí)總代碼量是2000多行,第一次寫這么多代碼的程序覺得很有成就感。現(xiàn)在看起來那時(shí) 做的是標(biāo)準(zhǔn)垃圾,但是當(dāng)時(shí)覺得還是很不錯(cuò)。用了Servlet + JSP。其實(shí)Servlet也不是用來當(dāng)控制器的,而是和JSP做的差不多功能,都是作view的功能的。很快,畢業(yè)設(shè)計(jì)交差過去了,寫寫畢業(yè)論文,準(zhǔn)備 答辯。在這個(gè)過程中,我又一次考慮自己下面該看什么書。

          這次我又看中了侯捷翻譯的一本巨著,也就是鼎鼎大名的Martin Fowler寫的《重構(gòu)——改善既有代碼的設(shè)計(jì)》這本書。剛開始聽見重構(gòu)這個(gè)名字,總覺得非常高深,加上都評(píng)論說重構(gòu)是和設(shè)計(jì)模式齊名的東東,感覺更加高 深恐怖了。大概在6月初我開始看了重構(gòu),剛開始看的時(shí)候雖然抱著試試看的心態(tài),不過還是非常認(rèn)真的。但是,讓我頗感意外的是重構(gòu)并不是很難,至少這本書中 說的非常通俗易懂,通過大量的實(shí)例讓你覺得重構(gòu)是種很簡(jiǎn)單很基本的技術(shù)。雖然我看完了重構(gòu)以后在真實(shí)的代碼編寫中很少直接按照上面代碼所說的方法進(jìn)行重構(gòu) 代碼,基本上都是通過IDE來重構(gòu)代碼,但是卻大大提升了自己編程思維,從此以后寫代碼就很少瞻前顧后了,因?yàn)槲覔碛辛酥貥?gòu)這個(gè)工具。這本書有點(diǎn)厚,再加 上中間有答辯,拍畢業(yè)照,以及畢業(yè)手續(xù)等等,這本書我花了一個(gè)半月看完。我看書的速度也不算快,不過我看書比較有恒心,不像有部分人看幾天就不想看了,我 能堅(jiān)持天天看,所以總的來說還是不慢的。我計(jì)算過,如果我每天看10頁(yè)書,堅(jiān)持下去,那一年就是3650頁(yè)書,平均一本書365頁(yè)來算,1年就是10本。 如果這10本書中有8本不屬于垃圾書籍,那么你這年就能有非常大的提高了。

          看重構(gòu)這本書中間我也抽了一段時(shí)間看了兩本其他的書,第一本是 《Java夜未眠》,挺不錯(cuò)的一本書,雖然是散文,但是還是能讓你明白不少道理,受益匪淺。另外一本就是李維的《Borland傳奇》,由于自己當(dāng)時(shí)最喜 歡用的工具就是JBuilder,所以也對(duì)Borland公司非常敬仰,特別對(duì)安德森,簡(jiǎn)直就頂禮膜拜啊,哈哈。這本書寫得很精彩,寫了Borland公 司二十年來的血淚史,寫了如何跟微軟斗爭(zhēng),如何在微軟和IBM的夾縫中生存。當(dāng)然,也有很多的對(duì)于技術(shù)方面作者李維自己的見解,看了會(huì)有不少同感的。就這 樣,磨磨蹭蹭地把重構(gòu)看完了。

              當(dāng)看完了《重構(gòu)》這本書之后,我也開始去公司報(bào)到上班了。可以看出來,我當(dāng)時(shí)工作的時(shí)候水平也很有限,但總比一年前要好不少,至少很多東西都已經(jīng)知道了。 那時(shí)外面極限編程聽的比較多,自己也去書店買了本《Java極限編程》回來看,現(xiàn)在想想算是我看得第一本垃圾書籍了。不過也是有收獲的,這本書極限編程也 就說了點(diǎn)概念,然后就寫了不少工具的使用方法。在看《重構(gòu)》中對(duì)JUnit有了點(diǎn)認(rèn)識(shí),不過只是皮毛中的皮毛。看了這本《Java極限編程》后對(duì) JUnit的使用又了解了不少皮毛,對(duì)于Cactus有了點(diǎn)了解,對(duì)Ant了解了不少,至少可以自己寫出自己需要的配置文件了,并且可以結(jié)合JUnit生 成測(cè)試Report。由于我去的是日企,做對(duì)日外包的,所以公司開始培訓(xùn)日本語(yǔ),用的是《標(biāo)準(zhǔn)日本語(yǔ)》這套教材。我于是邊學(xué)日語(yǔ)邊看技術(shù),大概2個(gè)星期左 右我把那本《Java極限編程》初步看完后就扔在了家里。這時(shí)的我已經(jīng)開始會(huì)用Ant了,覺得是步入J2EE的重要一步。

              很快啃掉那本垃圾書以后又看了本和Java不是非常有關(guān)的書:《程序員修煉之道——從小工到專家》,原因其實(shí)很簡(jiǎn)單,大學(xué)同學(xué)都說這本書是經(jīng)典書,看書這 東西,別人的評(píng)價(jià)還是能起不少作用的。這本書字?jǐn)?shù)不是很多,不過排版的時(shí)候比較分散,導(dǎo)致書本有點(diǎn)厚,呵呵,可能也算出版社賺錢的一種方法吧。不過總的來 說,我覺得出版社紙張質(zhì)量最好的是電子工業(yè)出版社,其次是中國(guó)電力出版社,最爛的恐怕就是機(jī)械工業(yè)出版社了,機(jī)械工業(yè)出版社有少量書紙張還能說過去,但有 不少簡(jiǎn)直讓人不得不有脾氣啊,紙張薄得感覺和寫毛筆字的宣紙都差不多了。這本電子工業(yè)出版社的書紙張質(zhì)量的確不錯(cuò),不過也許是因?yàn)槲夜αι袦\,所以這本書 雖然都看懂了,但是深有感觸并且銘記于心的沒有幾個(gè),現(xiàn)在再回想,也只記得軟件模塊設(shè)計(jì)時(shí)要正交等等少數(shù)幾點(diǎn)了。這本書由于內(nèi)容不是非常多,所以我就看了 半個(gè)月不到搞定。這時(shí)的我開發(fā)IDE已經(jīng)轉(zhuǎn)移到了Eclipse上,畢竟商業(yè)開發(fā)用D版有點(diǎn)說不過去,而且公司也怕查,所以不允許用JBuilder,鼓 勵(lì)大家用Eclipse。我用了一段時(shí)間的Eclipse后,從一開始的不適應(yīng)到后來覺得Eclipse很方便使用,JBuilder比Eclipse多 的就是一些根據(jù)不同類型開發(fā)的模版而已,而這些可以由Eclipse的插件來彌補(bǔ)。到了這時(shí),我覺得我的Java基礎(chǔ)應(yīng)該算還可以的了,API也熟悉了非 常多。我覺得看《Effective Java》的時(shí)機(jī)成熟了。

              由于大學(xué)已經(jīng)畢業(yè)了,所以也不會(huì)有同學(xué)的《Effective Java》放在邊上讓我看這樣的好事出現(xiàn),老老實(shí)實(shí)地去了書店買了本《Effective Java》中文版回來研讀。呵呵,大家也許會(huì)問我為什么不買本E文的看,雖然我大學(xué)早早也把英語(yǔ)4級(jí)過了,而且大學(xué)中不少計(jì)算機(jī)專業(yè)課程教材也是E文的, 當(dāng)時(shí)為了考試也認(rèn)真讀了。但是畢竟英語(yǔ)不是我們的母語(yǔ),看起來速度上明顯比中文版的慢一截。當(dāng)然,如果是那種垃圾翻譯者用機(jī)器翻譯出來的中文版,看那些垃 圾中文版速度肯定比不上直接看英文原版的。這時(shí)的我看《Effective Java》已經(jīng)不再是當(dāng)初的那么感覺很陌生了,覺得上面說的那些要點(diǎn)自己想想還都是可以理解的。我個(gè)人覺得提高自身編程習(xí)慣以及水平最多的還是看類似于 《Practical Java》和《Effective Java》的這種按照條目來進(jìn)行說明的書,能把你自己平時(shí)容易忽略的地方按照重點(diǎn)一個(gè)個(gè)揪出來進(jìn)行修正。比如《Effective Java》中的第一條,使用靜態(tài)工廠來代替構(gòu)造函數(shù),自己原來在進(jìn)行開發(fā)的時(shí)候,從來不怎么會(huì)主動(dòng)想到建立一個(gè)靜態(tài)工廠,總覺得使用構(gòu)造函數(shù)來新建一個(gè)對(duì) 象是天經(jīng)地義的事情。但看完這個(gè)條目后,我的看法也隨之改變,發(fā)現(xiàn)靜態(tài)工廠還是非常好的,當(dāng)然,也不是什么地方用靜態(tài)工廠都很好。上面也寫到了靜態(tài)工廠的 優(yōu)缺點(diǎn),比如在什么地方適合使用,什么場(chǎng)合最好不要使用等等。這本書我覺得翻譯的也不錯(cuò),絕對(duì)值,強(qiáng)烈向有一定開發(fā)經(jīng)驗(yàn)的人推薦。我大概看了3周半的樣子 把這本書看完,這時(shí)的時(shí)間也到了2004年的9月初,新員工入司培訓(xùn)也不再是第一個(gè)月純粹的日語(yǔ)培訓(xùn),而是技術(shù)培訓(xùn)和日語(yǔ)培訓(xùn)一起開展,技術(shù)上培訓(xùn) Java,Web開發(fā),數(shù)據(jù)庫(kù)開發(fā)這三門課程,日語(yǔ)則開始進(jìn)行日本語(yǔ)國(guó)際三級(jí)的培訓(xùn)。公司的日語(yǔ)培訓(xùn)和技術(shù)培訓(xùn)都還不錯(cuò),技術(shù)培訓(xùn)純粹把大家當(dāng)作什么都不 懂的人,在Java上從最原始的Hello World開始培訓(xùn),Web開發(fā)上從HTML頁(yè)面開始培訓(xùn),數(shù)據(jù)庫(kù)開發(fā)則從Oracle的安裝,SQL語(yǔ)句的編寫開始培訓(xùn)。當(dāng)然,在培訓(xùn)的過程中我也不會(huì) 閑著,而是又開始尋找自己要啃的書本,這次,我選中了Oreilly新出版不久的《Java與XML》第二版。

              由于XML表達(dá)數(shù)據(jù)的自由性以及強(qiáng)大型,所以XML特別適合于做配置文件以及數(shù)據(jù)信息文件,在Java中XML的使用可謂是多如牛毛。在J2EE中,從 Web Application的web.xml開始就是XML文件,到下面的Framework配置等等,沒有一個(gè)沒有XML的身影,而且XML都起到了舉足輕 重的作用。雖然我原來也懂一點(diǎn)XML,不過也僅限于XML的語(yǔ)法以及結(jié)構(gòu)等等,那些深入下去的東西基本還是盲點(diǎn),關(guān)于Java中如何處理XML更是一竅不 通。為了更好的學(xué)習(xí)J2EE,XML是必須征服得一座山峰。這次,我依然又再一次信任了Oreilly出版社,買了本當(dāng)時(shí)出版不久的《Java與XML》 中文第二版。這本書剛開始并沒有過多介紹XML本身過多的東西,只是為了怕某些讀者并不了解XML而對(duì)XML語(yǔ)法結(jié)構(gòu)等做了非常簡(jiǎn)要的介紹,不過也非常到 位的介紹。介紹完了這些XML基礎(chǔ)知識(shí)后就開始了SAX——〉DOM——〉JDOM——〉JAXP——〉Web Service的歷程。不過我現(xiàn)在覺得如果能介紹DOM4J就更好了,因?yàn)槲椰F(xiàn)在覺得DOM4J是Java中最好用而且性能也不錯(cuò)的XML處理工具。剛開 始的我其實(shí)什么是SAX,什么是DOM都不知道,對(duì)JAXP更是一無(wú)所知。這本書英文版據(jù)說很受好評(píng),中文版我只能說一般,因?yàn)橛行┑胤焦烙?jì)譯者并不擅長(zhǎng) 這一塊,所以翻譯得很生硬,以至于部分段落難于理解。總體來說,書的絕大多數(shù)內(nèi)容還是可以看懂,由于沒有具體實(shí)際操作的經(jīng)驗(yàn),所以很多也就是把概念理解 了,直到幾個(gè)月后做正式項(xiàng)目開始應(yīng)用這些XML處理工具進(jìn)行開發(fā)的時(shí)候才達(dá)到了熟練運(yùn)用的能力。在這本書中學(xué)會(huì)了JDOM的使用方法,JDOM也還是比較 好用的,學(xué)會(huì)了JDOM,以后操縱XML也方便了許多。這本書我的建議就是,可以一口氣讀到第十章JAXP部分,后面的Cocoon以及SOAP等等部分 那本書介紹的并不是很好。Cocoon我是看了官方專門的幫助文檔以后才感覺入了門。而SOAP是經(jīng)過別的書籍加上項(xiàng)目中的實(shí)際運(yùn)用才真正學(xué)會(huì)的。

          這 時(shí)到我剛進(jìn)公司已經(jīng)兩個(gè)月過去了,時(shí)間已經(jīng)到了9月中旬的樣子,還有一個(gè)月我們公司新員工入司培訓(xùn)就要結(jié)束,也意味著還有一個(gè)多月我們就要開始接觸正式項(xiàng) 目。這時(shí)的我寫B(tài)/S程序僅僅是JSP + JavaBean的水平,連JSP中的TAG都不會(huì)自定義,看見別人網(wǎng)上的程序自己還自己定義Tag很是羨慕,于是決定把那本《JSP設(shè)計(jì)》認(rèn)真看完,把 自定義Tag的功能實(shí)現(xiàn)。后來看了以后發(fā)現(xiàn)原來那本《JSP設(shè)計(jì)》的精華都在最后的150頁(yè)內(nèi),最后那部分先是介紹了自定義Tag的定義方法以及Tag定 義所帶來的一些好處。自從學(xué)會(huì)了如何自定義Tag,在后來公司的項(xiàng)目中自己也根據(jù)項(xiàng)目的特點(diǎn)定義了一些共通的Tag,大大方便了不少項(xiàng)目中的開發(fā)人員,提 高了生產(chǎn)力。這本書而且也說了一下B/S開發(fā)的兩種Web Module。在這里,我第一次知道了Web開發(fā)可以用一個(gè)Servlet作為控制器,用JSP僅僅作用于表現(xiàn)層,這也為以后掌握MVC打下了很好的基 礎(chǔ)。

          9月中下旬掃完了《JSP設(shè)計(jì)》的尾巴后,有一次跟公司給我們培訓(xùn)的老師在閑聊時(shí)談到了項(xiàng)目開發(fā),我詢問他項(xiàng)目是不是用JSP和 JavaBean來開發(fā),他笑著和我說不是這樣的,而是基于Framework來進(jìn)行開發(fā)。比如Struts就是公司的常用Framework。 Struts這東西以前也好像聽說過,不過從來也只是聽說而已,并沒有看過。得到這個(gè)信息的我,為了能盡快熟悉實(shí)際項(xiàng)目的開發(fā)環(huán)境,便決心盡快學(xué)會(huì) Struts。當(dāng)時(shí)的市場(chǎng)上講解Struts的書只有一本,也就是Oreilly的《Jakarta Struts編程》,不像現(xiàn)在連《Struts in Action》的中文版也有了。我去了書店買來開始回家看,剛開始看的時(shí)候覺得如同云里霧里一般,因?yàn)檫@本書歸納總結(jié)性的東西很多,比較適合當(dāng)參考手冊(cè), 而真正帶領(lǐng)新手入門這一塊做的并不好。所以當(dāng)我把這本書都看完了以后,還是不會(huì)用Struts編寫一個(gè)程序,只是感覺自己朦朦朧朧懂了一些概念,比如 MVC什么的。在公司我們的培訓(xùn)也結(jié)束了,通知在國(guó)慶節(jié)過來以后的第一個(gè)星期——大概是10月10日左右進(jìn)行考試,最后根據(jù)培訓(xùn)考核情況來調(diào)整薪水。當(dāng)時(shí) 跟我一起培訓(xùn)的新員工基本上沒有人會(huì)Struts,其實(shí)這個(gè)時(shí)候連會(huì)用JSP + JavaBean寫一個(gè)最簡(jiǎn)單的登錄畫面的人也沒有多少個(gè),大部分人還是模模糊糊懂一點(diǎn),但是具體做東西還是做不來的那種水平。國(guó)慶節(jié)大概10月5號(hào)的我 去了趟書店,突然發(fā)現(xiàn)書架上新上了一本書,就是孫衛(wèi)琴編寫的《精通Struts》這本書。孫衛(wèi)琴的書我倒是聽說過,就是在這之前出的一本關(guān)于Tomcat 以及Web App開發(fā)的書,據(jù)說挺容易上手的。我翻看了這本書的目錄結(jié)構(gòu),覺得可以值得一讀,于是雖然價(jià)格不菲,仍然買回家當(dāng)天就研讀起來。憑我的讀后感覺來說,這 本書也許學(xué)術(shù)價(jià)值并不高,說得深入的東西基本沒有,但是特別適合初學(xué)者,通過Hello World這種例子迅速讓你手把手編寫出第一個(gè)Struts程序。就這樣,在這本書買回來的第二天,我自己就用Struts寫了一個(gè)很簡(jiǎn)單的登錄畫面程 序,當(dāng)時(shí)的感覺別提多興奮了,就感覺自己入了門,以后的道路一片光明。在這里,我要由衷地感謝孫衛(wèi)琴女士,寫了這么一本適合初學(xué)者的書,同時(shí)也建議沒有學(xué) 過Struts但又想掌握Struts的Java程序員們買這本書回來看(不知道我是不是有書托之嫌,我只是說我自己的心里想法)。

              國(guó)慶的假期放完了,我也回到了公司準(zhǔn)備考核,上午是筆試,下午是上機(jī)考試。筆試分為了4塊,分別是Java,Web開發(fā),Oracle數(shù)據(jù)庫(kù),以及 CMMI規(guī)約。這四門除了Oracle數(shù)據(jù)庫(kù)我一向不是很擅長(zhǎng),只考了個(gè)中等分?jǐn)?shù)以外,其他三門分?jǐn)?shù)都名列前茅。不過CMMI規(guī)約老實(shí)說我也不怎么會(huì),不 過碰巧考的很多都是我知道的東西。下午是上機(jī)考試,題目給出來了,我一看題目,原來是一個(gè)最簡(jiǎn)易的成績(jī)查詢系統(tǒng),也就是數(shù)據(jù)庫(kù)里面已經(jīng)有一些學(xué)生成績(jī),我 們寫一個(gè)檢索頁(yè)面,可以輸入或者選擇檢索條件,把符合我們檢索條件的數(shù)據(jù)輸出并顯示在畫面中。我于是拿剛學(xué)會(huì)不久的Struts進(jìn)行編寫,在3個(gè)小時(shí)內(nèi)把 整個(gè)頁(yè)面都寫好了,并且還自定義了一個(gè)Tag來顯示數(shù)據(jù)信息。考完以后我才知道總共也就五六個(gè)人程序可以運(yùn)行,而且只有我一個(gè)人用的是Struts,其他 人基本都是最簡(jiǎn)單的JSP + JavaBean,有的人連JavaBean都沒有,數(shù)據(jù)庫(kù)操作全部寫在了JSP頁(yè)面中。毫無(wú)疑問,這次上機(jī)考試我得到了好評(píng),給了最高分。在全部的培訓(xùn) 成績(jī)中我也居前兩名,我們部門新員工我排第一名。帶著這個(gè)成績(jī),我們的入司培訓(xùn)基本結(jié)束,開始進(jìn)入部門做實(shí)習(xí)項(xiàng)目。

              雖然說我們正式進(jìn)了部門,不過試用期還沒有結(jié)束,我們?cè)囉闷谧詈笠粋€(gè)月的任務(wù)就是做一個(gè)實(shí)習(xí)項(xiàng)目,當(dāng)然,每天還是要進(jìn)行日語(yǔ)培訓(xùn),因?yàn)橐獏⒓?2月份的國(guó) 際日語(yǔ)三級(jí)考試。公司也象征性得給大家培訓(xùn)了三節(jié)課的技術(shù),第一節(jié)是Struts培訓(xùn),第二節(jié)是Web App的MVC結(jié)構(gòu)的培訓(xùn),第三節(jié)是Log4j培訓(xùn),這幾次培訓(xùn)下來,大部分人感覺好象云里霧里一樣,基本什么都沒聽懂,不過我由于有了點(diǎn)Struts的 基本知識(shí),所以感覺收獲比較大,特別是MVC的培訓(xùn)中我真正明白了視圖——控制器——模型這三層每層應(yīng)該怎么處理,知道了一個(gè)Web App中如何分Java Package比較好,明白了專門有一個(gè)DAO層來處理數(shù)據(jù)庫(kù)所帶來的便捷,明白了Log在Web開發(fā)中的重要地位,這為以后的開發(fā)帶來了很大的好處。實(shí) 習(xí)項(xiàng)目的課題很快就下來了,要我們做一個(gè)電子相冊(cè)的B/S系統(tǒng),要求有圖片上傳,圖片檢索,圖片顯示以及要用Struts來構(gòu)建,這些是基本的要求,其他 功能可以自由擴(kuò)張。我們部門的新員工分為兩個(gè)小Group,都是一樣的課題,互相促進(jìn)和學(xué)習(xí),每個(gè)Group還配備了一個(gè)老員工,作為督促我們的進(jìn)度,防 止我們有過大的偏差等等,不過具體做東西上原則上要求是不會(huì)給我們什么幫助。首先每個(gè)小Group要選出一個(gè)Leader,結(jié)果我被大家一致選為我們 Group的Leader。在小組討論中我們先進(jìn)行需求分析,大家的討論很是熱烈,主意也很多,不過基本上組員們也都是點(diǎn)子多,具體實(shí)現(xiàn)上面還都沒有想 過。對(duì)于他們的那些建議,絕大多數(shù)我決定都作為我們要實(shí)現(xiàn)的目標(biāo),但也有少部分我覺得目前以我們的水平還無(wú)法實(shí)現(xiàn)的,就先否決了。會(huì)議開完后,當(dāng)天回家以 后我就開始排開發(fā)計(jì)劃和個(gè)人的進(jìn)度,第二天寫畫面的基本設(shè)計(jì),第三天把組員拉過來開始Review基本設(shè)計(jì),我們組的速度還算比較快。從星期二公布課題, 到星期五就和幾個(gè)組員一起把DEMO畫面設(shè)計(jì)出來了。原來的計(jì)劃是第二個(gè)星期一開始Coding,大概花一個(gè)星期完成。不過其余組員似乎還是不怎么會(huì) Struts,于是我回家星期六星期天基本全天都在看書寫代碼學(xué)習(xí),花了兩天把項(xiàng)目基本Coding完畢。其中Web頁(yè)面部分也不再使用一開始使用 Frame的做法,而是采用了Tiles框架。Tiles的使用過后我感覺是非常好的東西,經(jīng)過簡(jiǎn)單的配置可以完成大批網(wǎng)頁(yè)中類似部分的構(gòu)建,而且生成的 屬于一個(gè)頁(yè)面,這樣就省去了以前寫Frame時(shí)提交頁(yè)面總是要考慮設(shè)置Target以及在引用對(duì)象的時(shí)候大批Parent或者top對(duì)象使用的麻煩事了。 在開發(fā)過程中我使用了Log4j,這為我的調(diào)試程序帶來了極大的方便,呵呵,可以想象,沒有Log來調(diào)試一個(gè)Web程序真是不可想象的。

          這 段時(shí)間我是邊開發(fā)邊翻查那本《精通Struts》的,這樣,迅速把Struts中提供的許多Tag弄熟練了,為以后具體的項(xiàng)目開發(fā)帶來了便捷。也許是一向 以來公司的實(shí)習(xí)項(xiàng)目完成效果都不是很理想吧,這次我們的迅速完成比較出乎Leader的意料,綜合三個(gè)月的試用培訓(xùn),由于我在日語(yǔ)和技術(shù)以及實(shí)習(xí)項(xiàng)目中表 現(xiàn)都還不錯(cuò),所以定工資級(jí)別時(shí)也是同一批進(jìn)公司的新員工中最高的,隨著轉(zhuǎn)正會(huì)議的結(jié)束,我也在10月底成為了公司的正式員工。大概剛剛進(jìn)入11月份,我們 Group便開動(dòng)一個(gè)項(xiàng)目,項(xiàng)目不是很大。當(dāng)時(shí)老員工們?cè)S多都在做項(xiàng)目的詳細(xì)設(shè)計(jì),我便跟著公司一位技術(shù)專家(也是當(dāng)初給我們?nèi)胨九嘤?xùn)的其中一位老師)做 起項(xiàng)目的Framework構(gòu)建工作。當(dāng)時(shí)的我才進(jìn)公司,第一資歷尚淺,第二我的確也并不是很會(huì)什么東西,所以給我的任務(wù)很多都是一些模塊的 Utility的設(shè)計(jì)。比如通用的Check方法啊,CSV以及定長(zhǎng)文件的讀取解析什么的啊,還有某些在IE中可以實(shí)現(xiàn)的效果如何在Netscape中也 能實(shí)現(xiàn)同樣的效果等等。雖然這些東西在現(xiàn)在看來并不是很復(fù)雜,但是當(dāng)時(shí)自己的確隨著做這些東西而學(xué)到了很多很多。比如使用JDOM對(duì)XML文件的解析啊, 很多Check方法的小技巧啊,IE和Netscape究竟有什么地方不一致,該如何解決等等,這些都在這幾天內(nèi)了解了很多。在這幾天中,我通過網(wǎng)上查找 資料,臨場(chǎng)迅速學(xué)習(xí)了Java反射的使用方法,并且自己邊學(xué)邊寫實(shí)例,實(shí)現(xiàn)了各種情況下的反射案例。我個(gè)人覺得掌握J(rèn)ava反射技術(shù)是非常重要的,這讓你 可以寫一些通用的工具。如果不會(huì)反射技術(shù)的話,也許你永遠(yuǎn)只能寫一些針對(duì)特定情況下的解決方法。而會(huì)使用反射以后,你可以寫一些代碼,這些代碼可以用在許 多地方,達(dá)到自己擴(kuò)展甚至構(gòu)建Framework的效果。在這個(gè)項(xiàng)目中,我使用了自定義Tag和Java反射技術(shù),定義了些項(xiàng)目中比較需要的通用的 Tag,方便了大家。

              后來聽老員工說新員工進(jìn)公司就開始做Framework是以前從來都沒有過的事情,因?yàn)槲覀僉eader對(duì)我希望比較大,所以想盡可能培養(yǎng)我,讓我早點(diǎn)挑 起項(xiàng)目大梁,所以給我的成長(zhǎng)提供了一次又一次的機(jī)遇。11月中旬以后,項(xiàng)目開始進(jìn)入編碼階段,我也第一次看到了正式的項(xiàng)目設(shè)計(jì)書。第一次看到設(shè)計(jì)書的時(shí)候 我都覺得自己腦子有點(diǎn)懵,一大堆日語(yǔ)什么含義自己不是很清楚,而且感覺根本無(wú)從下手,不知道從哪里開始看比較好。項(xiàng)目擔(dān)當(dāng)耐心得和我說了設(shè)計(jì)書的格式以及 究竟什么地方是什么一個(gè)含義,以及Coding的時(shí)候按照什么個(gè)思路來看設(shè)計(jì)書。再加上項(xiàng)目中有老員工先寫了個(gè)Sample,讓大家看了標(biāo)準(zhǔn)的一個(gè)流程, 所以我們就依葫蘆畫瓢,慢慢得把一個(gè)畫面一個(gè)畫面Coding完畢。當(dāng)然了,后來也有測(cè)試員來測(cè)試我們的畫面,發(fā)現(xiàn)bug后就發(fā)Bug Report給我,那一個(gè)月就是在Coding,修正Bug中渡過的,這個(gè)項(xiàng)目是用Struts做的,因?yàn)椴淮蟆K砸矝]有再用其他的 Framework,數(shù)據(jù)庫(kù)操作那里只有個(gè)非常簡(jiǎn)單的單表操作DAO層,其余的DB操作都是自己通過JDBC操作語(yǔ)句來完成的。在這第一個(gè)自己接觸的真正 項(xiàng)目中,我自己學(xué)到了很多B/S設(shè)計(jì)的技巧,感覺很充實(shí)。不過書本學(xué)習(xí)方面我也沒有閑著,我為了能夠深入了解Java,大概在11月中旬左右開始看《深入 Java虛擬機(jī)》這本書,由于內(nèi)容比較深入,所以看得也有點(diǎn)吃力。書翻譯得和寫得都還不錯(cuò),值得一看,我一直看了前八章,看到Java程序運(yùn)行細(xì)節(jié)后就沒 再看了,大概看到了12月底的樣子吧,呵呵,有時(shí)間的話決定把后面的部分也看完。這本書看完后收獲就是了解了Class文件的實(shí)質(zhì),Java的安全模型, 虛擬機(jī)是如何工作的。這些知識(shí)對(duì)后來調(diào)試程序Bug或者Exception的時(shí)候非常有好處,可以把以前自己覺得莫名其妙的錯(cuò)誤的原因找出來,不像以前遇 到很古怪的Exception的時(shí)候怎么死的都不知道,從讀完這本書以后,在以后的調(diào)試異常中很少再有不知所以然的感覺了。

              2004年12月底的時(shí)候,我的第一個(gè)項(xiàng)目也做完了,由于我空閑著,Leader便在星期三的時(shí)候把一個(gè)公司內(nèi)部開發(fā)的Source統(tǒng)計(jì)的小工具讓我進(jìn)行 修改,使得添加一個(gè)比較有用的功能。東西給我的時(shí)候基本沒有任何文檔,在我手上的就是一堆源代碼而已,界面是用Swing制作的,因?yàn)闆]有專門在UI上進(jìn) 行精心設(shè)計(jì),所以說不上好看,典型的Java編寫的圖形界面的程序的樣子。軟件不是非常大,估計(jì)在1萬(wàn)行源代碼以內(nèi),不過對(duì)于只有一個(gè)人修改來說,也比較 夠嗆了。還好我在剛學(xué)Java的時(shí)候用JBuilder寫了一些Swing的程序,現(xiàn)在還是對(duì)Swing有概念的,所以拿到手上以后經(jīng)過仔細(xì)分析,逐漸理 清了頭緒。經(jīng)過修改和自己測(cè)試完畢后,覺得還比較滿意,達(dá)到了預(yù)期的目標(biāo),于是在星期五的時(shí)候提交給了Leader。通過這次,對(duì)Swing的開發(fā)又加深 了印象,自然,在有的細(xì)節(jié)技巧方面受益匪淺。

          元旦很快來臨了,在年底以前,公司覺得有必要學(xué)習(xí)下Hibernate,雖然我們目前的項(xiàng)目中 還沒有用過Hibernate,而是用另外一個(gè)公司內(nèi)部開發(fā)的ORM工具,不過幾名技術(shù)專家初步對(duì)Hibernate感覺后覺得Hibernate的功能 要強(qiáng)大的多,而且是開源的,不斷有人在推動(dòng),升級(jí),所以有必要我們要學(xué)Hibernate。這次的學(xué)習(xí)采用學(xué)習(xí)小組的形式,也就是公司內(nèi)部先抽幾名員工 (主要是技術(shù)部門的,當(dāng)然,開發(fā)部門如果有興趣的話也可以考慮)來進(jìn)行深入學(xué)習(xí),然后定期開會(huì)交流互相學(xué)習(xí),達(dá)到短時(shí)間內(nèi)先行的幾名成員迅速深入掌握 Hibernate的形式。由于我第一處于空閑狀態(tài),第二也比較有興趣,而且跟技術(shù)部門的專家們也比較談得來,所以我也加入了其中,成為幾名學(xué)習(xí)小組中成 員的一部分。我們學(xué)習(xí)資料主要就是《Hibernate in Action》英文版一書以及官方的幫助手冊(cè)。我負(fù)責(zé)其中對(duì)象操作,Transaction和Cache,還有高級(jí)Mapping關(guān)系的設(shè)置幾個(gè)部分的學(xué) 習(xí)。由于資料都是全英文的,所以看書速度并不是很快,不過還是初步得到掌握了。大概學(xué)習(xí)了半個(gè)多月的樣子,我們各自基本學(xué)習(xí)完畢,互相交流后并且寫下了讀 書筆記,用來后面具體項(xiàng)目開發(fā)時(shí)候參考用。通過這大半個(gè)月的學(xué)習(xí),我個(gè)人覺得提高了非常多,在這之前,我只知道有ORM這種東西,但是從來沒有使用過,也 從來沒有看過。自從學(xué)過了以后,我不僅對(duì)Hibernate在代碼編寫時(shí)的使用比較熟悉了,而且對(duì)Hibernate的配置以及許多底層的知識(shí)有了很清楚 的認(rèn)識(shí),讓自己在數(shù)據(jù)持久化方面的認(rèn)識(shí)提高了大大的一步。

              元旦過后,雖然一邊在學(xué)習(xí)Hibernate,不過由于下面項(xiàng)目的需要,Leader跟我說要我學(xué)一下Unix下的Shell編程,因?yàn)? 項(xiàng)目中許多批處理會(huì)用Shell來啟動(dòng)。UNIX命令在學(xué)校時(shí)候?qū)W過的,不過這個(gè)時(shí)候已經(jīng)忘記了很多,只是翻閱資料的時(shí)候還能回想起來不少命令。 Shell并不難,如果有了編程基礎(chǔ),學(xué)習(xí)Shell編程也很快的,總體感覺就是編程語(yǔ)言大同小異,從基本語(yǔ)法來說,不外乎賦值、條件、循環(huán)這幾種類型。 只要迅速掌握這幾種類型在這種編程語(yǔ)言中的編碼格式,那么你就可以迅速掌握這門語(yǔ)言最基本的編程能力。Shell經(jīng)過一周的學(xué)習(xí)后覺得感覺不錯(cuò),不僅可以 順利看懂別人寫的Shell程序,而且自己可以在Linux下順利寫出符合自己需求的Shell程序并能順利執(zhí)行。但是突發(fā)事件總是有的,那個(gè)項(xiàng)目突然決 定延后兩個(gè)月,所以前一個(gè)星期的學(xué)得Shell等于暫時(shí)派不上用場(chǎng)了。不過嘛,多學(xué)一樣技能總是沒有害處的,而且又復(fù)習(xí)了那么多Unix命令啦,感覺還是 很不錯(cuò)的。于是我又進(jìn)入了不在項(xiàng)目中的真空期了。

              但是好景不長(zhǎng)啊,好日子還沒有過上兩個(gè)星期,公司去年做的一個(gè)比較大的項(xiàng)目開始了2期開發(fā),我也被一下拖入了項(xiàng)目中。說起那個(gè)項(xiàng)目,公司好多人還心有余 悸,據(jù)說去年那個(gè)項(xiàng)目開發(fā)的時(shí)候,大概50多號(hào)人干了好幾個(gè)月,每天都是11點(diǎn)以后才有可能回家,周六是鐵定加班,周日是看情況,晚上就算加班加到凌晨3 點(diǎn)也不是什么奇怪的事情。當(dāng)時(shí)大家都說多來幾個(gè)這種項(xiàng)目大家就要死了,這次這個(gè)項(xiàng)目的2期過來了,大家精神又一次緊張起來咯。一開始照例是項(xiàng)目會(huì)議,聽完 項(xiàng)目經(jīng)理描述以后,大家也放心了不少,這次雖然說是二期,不過規(guī)模不大,只需要15個(gè)人左右干一個(gè)月就能搞定。主要是把項(xiàng)目一期里面一些地方進(jìn)行改修,當(dāng) 然也有需要新的畫面的開發(fā),不過相對(duì)于去年的那塊不是很多而已。對(duì)我來說這次是個(gè)很大的考驗(yàn),因?yàn)轫?xiàng)目是二期,項(xiàng)目組內(nèi)除了我,其他的人都做過1期開發(fā), 所以對(duì)項(xiàng)目結(jié)構(gòu)都很清楚。這次項(xiàng)目開始并沒有什么培訓(xùn),所以我只能單獨(dú)看1期的源代碼來熟悉項(xiàng)目結(jié)構(gòu)什么的。這個(gè)時(shí)候項(xiàng)目經(jīng)理把我叫去要我辦理護(hù)照,準(zhǔn)備 這個(gè)項(xiàng)目派遣我去東京現(xiàn)場(chǎng)維護(hù)。

          這個(gè)項(xiàng)目是個(gè)比較全面比較大的項(xiàng)目,服務(wù)器采取了集群的方式,數(shù)據(jù)量也是千萬(wàn)乃至上億級(jí)別的,所以性能要求 特別高。在技術(shù)方面用到了很多,使用EJB來控制Transaction,使用了ORM工具來操縱DB數(shù)據(jù)等等等等。而且由于比較龐大,所以服務(wù)器初始化 的那塊為了Load上去大批配置信息,代碼量極其龐大,在權(quán)限控制的那塊地方,代碼非常難以讀懂。這也給我一開始的學(xué)習(xí)代碼帶來了很大的一塊麻煩。不過總 算靜下心來后把整個(gè)項(xiàng)目框架以及實(shí)現(xiàn)手法基本摸清楚了,這個(gè)時(shí)候覺得非常開心,而且對(duì)Web應(yīng)用程序的構(gòu)造心里面也非常充實(shí),覺得自己已經(jīng)具備寫 Framework的初步能力了。

          項(xiàng)目是緊張的,基本上每天晚上都要加班到11點(diǎn),然后打車回家,哈哈,公司報(bào)銷。而且臨近過年,這么加班 也一點(diǎn)都感覺不到過年的氣息。不過我也不能因此放松了自己的學(xué)習(xí)。我覺得自己的基礎(chǔ)應(yīng)該算比較好了,便開始啃起另外一本大部頭——《Java與模式》。一 直以來我對(duì)設(shè)計(jì)模式的感覺就是一些已經(jīng)成型的,久經(jīng)考驗(yàn)的代碼框架,具有非常好的可擴(kuò)展能力以及非常好的代碼健壯性。不過初學(xué)者最好不要看設(shè)計(jì)模式,因?yàn)? 你接觸的代碼不多,如果貿(mào)然看設(shè)計(jì)模式的話,會(huì)造成不明白為什么這種設(shè)計(jì)模式好,究竟好在什么地方的情況下就在代碼中亂套設(shè)計(jì)模式,對(duì)自己的以后編碼發(fā)展 帶來不利的影響。每一種設(shè)計(jì)模式都有每一種設(shè)計(jì)模式的特點(diǎn),自然也有他們自身的適用范圍,比如拿最基本的單例模式(Singleton)來說,適合于做配 置信息讀取器,主鍵產(chǎn)生器等全局唯一的東西。如果初學(xué)者不明白這些,拿單例模式亂套,什么都用單例模式,比如把普通傳遞數(shù)據(jù)用的JavaBean也做成了 單例模式,帶來的惡果就嚴(yán)重了。這本《Java與模式》我是從頭到尾認(rèn)認(rèn)真真看了,每看完一個(gè)模式都會(huì)仔細(xì)回想以前看的代碼哪里用到過這個(gè)模式,總會(huì)自己 想想這些模式適用于哪些地方。因?yàn)檫@個(gè)時(shí)候我自己編寫的代碼行數(shù)也已經(jīng)很多了,所以看見這些模式就會(huì)特別有感覺。經(jīng)過50多天的認(rèn)真研讀,所有模式都被我 消化了下去,并且使得我的對(duì)程序開發(fā)上面的認(rèn)識(shí)提升了非常大的一步。順路說一句,這本書寫得非常好,例子很多,但是不復(fù)雜,有一定代碼編寫經(jīng)驗(yàn)的人就可以 看懂了。不過看懂并不是最重要的,重要的是消化下去,用來理解以前看過的代碼的精華,這樣自己才能得到提高。

              這個(gè)項(xiàng)目雖然很緊張很忙,不過我還是適應(yīng)了下來,而且對(duì)整個(gè)項(xiàng)目結(jié)構(gòu)什么的都有了比較好的整體的把握。項(xiàng)目橫跨了整個(gè)過年期間,所以在過年的那幾天都必須 開著手機(jī),怕有什么突發(fā)事件要求去加班。簽證在2月4日左右送過去簽,Leader跟我說因?yàn)樵谶^年期間,所以簽證可能會(huì)比較緩慢,比較難簽,不過一般情 況下1個(gè)月應(yīng)該足夠了。原計(jì)劃我是跟另外兩個(gè)同事3月6日去東京,這樣算算也差不多。不過中國(guó)有句話叫好事多磨,呵呵,用在我身上的確不過分,我的簽證3 月3日日本領(lǐng)事館才簽,三月四日送到南京。3月5日和3月6日是雙休日,所以3月7日簽證才送到我手上。由于計(jì)劃是3月6日派人去東京,所以只好派另外一 個(gè)身上有簽證還沒有過期的人代替我過去,這次的機(jī)會(huì)就算泡湯咯。不過我并不是很在意,因?yàn)楣具@里去東京出差的機(jī)會(huì)狠多,特別對(duì)于開發(fā)人員,據(jù)說工作幾年 后一聽到去日本出差就不樂意,畢竟也背井離鄉(xiāng)么。

              在這個(gè)項(xiàng)目的途中,大概在2005年1月底2月初的時(shí)候公司也開始進(jìn)行了制作詳細(xì)設(shè)計(jì)的培訓(xùn),我雖然在項(xiàng)目中,不過也成為了其中一員。這次培訓(xùn)總共大概6 次課,每次2個(gè)多小時(shí),雖然時(shí)間不長(zhǎng),不過把詳細(xì)設(shè)計(jì)的要點(diǎn)以及思路和容易出錯(cuò)的地方都說了出來,感覺很是不錯(cuò),這幾次課的培訓(xùn)后,雖然可能要我立即進(jìn)行 詳細(xì)設(shè)計(jì)編寫還有點(diǎn)困難,不過心里面已經(jīng)有了不少底,我覺得經(jīng)過一段時(shí)間后的鍛煉,我應(yīng)該可以有進(jìn)行詳細(xì)設(shè)計(jì)的能力了。

              3月初這個(gè)大項(xiàng)目結(jié)束后,本以為可以休整下,不過很快通知我3月7日加入另外一個(gè)項(xiàng)目,其實(shí)也不算一個(gè)正式的項(xiàng)目,屬于公司知識(shí)庫(kù)的一個(gè)信息查詢模塊。由 公司的一個(gè)技術(shù)專家負(fù)責(zé),那人也就是我進(jìn)公司時(shí)候第一個(gè)項(xiàng)目中帶著我的那個(gè)技術(shù)專家,說起來我和他還真有緣,現(xiàn)在我這個(gè)項(xiàng)目還是跟著他,而且公司里面唯一 一個(gè)和我同月同日生的人,真是很巧的巧合呢。他人挺好,很熱心,所以我也從他那學(xué)到了很多東西。這次由于不是正式項(xiàng)目,所以并沒有什么基本設(shè)計(jì)書,而是他 給我們開會(huì)議的時(shí)候大致說了下項(xiàng)目的內(nèi)容,每個(gè)畫面的具體功能以及數(shù)據(jù)庫(kù)表格的設(shè)計(jì)。由于這次項(xiàng)目規(guī)模很小,總共就12個(gè)畫面的量,所以不采取 Struts等Framework,而是采用比較原始的JSP + JavaBeans的構(gòu)造。我們每個(gè)人根據(jù)他所跟我們講解得功能寫每個(gè)人自己分配到的畫面的詳細(xì)設(shè)計(jì),其實(shí)也不算真正的詳細(xì)設(shè)計(jì),就是每個(gè)人把自己操作的 那塊的具體邏輯設(shè)計(jì)寫出來,然后和他一起review一次,再開始編寫代碼。詳細(xì)設(shè)計(jì)這里我做的很快,當(dāng)天下午就把自己分配到的兩個(gè)畫面業(yè)務(wù)邏輯什么的都 寫好了,星期一布置得任務(wù),我星期三的時(shí)候全部編碼自測(cè)完畢提交,所以我的感覺就好像這個(gè)小項(xiàng)目一瞬間就結(jié)束了。

          日本每年財(cái)務(wù)結(jié)算是在3月 份,所以我們歷來的習(xí)慣就是每年1月和2月很忙,3月開始清閑,一直可以到5月左右會(huì)接到個(gè)大項(xiàng)目昨。所以接下來就真正到了我的空閑時(shí)期,沒有項(xiàng)目的壓 力,我可以自由學(xué)我自己喜歡的東西。很久以前買了本《精通EJB》第二版,可是一直以來我覺得自己功力尚淺,所以沒有看,這次我想認(rèn)真學(xué)學(xué)EJB。雖然大 家公認(rèn)EJB并不是很好,不過歷來受到批評(píng)的都是EJB中的Entity Bean部分,這部分我覺得可以借助Hibernate來彌補(bǔ),而會(huì)話Bean和消息驅(qū)動(dòng)Bean則還是挺不錯(cuò)的。這次也當(dāng)學(xué)一門技術(shù),學(xué)習(xí)其好的東西, 不是很好的東西就當(dāng)作以后開發(fā)時(shí)候的借鑒。《精通EJB》這本書我的感覺是書質(zhì)量比較好,不過翻譯的水平稍微差了點(diǎn),特別是有不少錯(cuò)誤,而且很低級(jí)的錯(cuò)誤 居然校對(duì)的時(shí)候都沒有發(fā)現(xiàn),不能不說是個(gè)比較大的瑕疵。但是它不失為一本EJB的好教材。從一開始的JNDI開始,然后講解了對(duì)象序列化,RMI- IIOP等等。這些以前都模模糊糊,或者是看過了但是還不知道究竟有什么用。但是經(jīng)過這次的學(xué)習(xí)以后,對(duì)這些分布式系統(tǒng)比較需要的東西有了進(jìn)一步的了解, 感覺頭腦中比較清晰,究竟RMI是什么樣的工作原理,怎樣實(shí)現(xiàn)一個(gè)遠(yuǎn)程方法調(diào)用等等。接下來的EJB學(xué)習(xí),自己用Eclipse + Weblogic邊看書邊動(dòng)手,寫了一個(gè)個(gè)自己的學(xué)習(xí)小程序。我個(gè)人感覺看書最好就是邊看邊自己動(dòng)手寫小學(xué)習(xí)程序,這樣比光看不練能學(xué)到多得多的東西。學(xué) 了EJB后覺得腦子又清晰了很多,看見一個(gè)案例后頭腦中就會(huì)有好幾種如何解決的方法,幾種方法互相在頭腦中自己比較,經(jīng)過這樣,大大提高了自己的思維活躍 性。

              3月中旬開始由于公司比較清閑,大部分人處于沒有項(xiàng)目的狀態(tài),所以公司舉辦了第一屆全公司范圍的編程競(jìng)賽。公司只指定了題目為一個(gè)日歷系統(tǒng),要求具有日程 記事等功能,其余功能自由發(fā)揮。這次不再采用團(tuán)隊(duì)形式了,而是采取各自為戰(zhàn)的策略。自從培訓(xùn)過詳細(xì)設(shè)計(jì)以后,我頭腦一直有如何寫詳細(xì)設(shè)計(jì)的思路,這次我自 己首先指定了開發(fā)計(jì)劃,保證自己控制自己的進(jìn)度。接著進(jìn)行了需求分析,確定了我有哪些功能。然后在自己的基本設(shè)計(jì)中開始進(jìn)行數(shù)據(jù)庫(kù)結(jié)構(gòu)設(shè)計(jì)。這次我決定采 用Hibernate+Struts的結(jié)構(gòu)進(jìn)行編寫,這樣我的數(shù)據(jù)持久層操作大大簡(jiǎn)化,而且功能上也增強(qiáng)了許多。DB設(shè)計(jì)好以后我開始DEMO畫面的制 作。說實(shí)話,我美工水平實(shí)在不怎么樣,可以說雖然一般網(wǎng)頁(yè)的效果我都會(huì)自己做出來,不過具體網(wǎng)頁(yè)設(shè)計(jì)成什么樣我還真是一竅不通。還好 Dreamweaver我還算算是比較熟練,自己搗鼓搗鼓也想摸象樣把DEMO畫面給設(shè)計(jì)出來了,不過美觀不美觀我就覺得不怎么樣了,只是我能力有限,也 沒辦法設(shè)計(jì)的更好看,這個(gè)時(shí)候我感受到了一個(gè)項(xiàng)目中美工是多么重要啊。下面的詳細(xì)設(shè)計(jì)自己寫得很開心,把需要的功能都用文字反映了出來,這也算我寫成詳細(xì) 設(shè)計(jì)樣子的第一份詳細(xì)設(shè)計(jì)了,做完挺有成就感的。接下來首先構(gòu)筑自己這個(gè)小項(xiàng)目的Framework,經(jīng)過公司兩個(gè)正式項(xiàng)目的洗禮后,那兩個(gè)項(xiàng)目的 Framework我都認(rèn)真研讀過源代碼的,所以我自己有了自己心里一套Framework的構(gòu)造方法,特別是如何把Struts和Hibernate結(jié) 合起來的結(jié)構(gòu),自己有自己的一些想法。在這次Framework構(gòu)造中,我沒有復(fù)制任何公司以前的代碼段,都是憑著自己對(duì)以前看的代碼理解后寫出來的。這 次項(xiàng)目我覺得對(duì)自己的提高也很大,首先鍛煉了自己詳細(xì)設(shè)計(jì)的能力。其次,自己雖然學(xué)習(xí)過Hibernate,不過從來沒有這么樣應(yīng)用過 Hibernate,這次讓自己大大提升了實(shí)踐運(yùn)用的經(jīng)驗(yàn)。公司由于知道這時(shí)也沒有一個(gè)真正的項(xiàng)目使用Hibernate,所以這時(shí)的我也算公司內(nèi)部 Hibernate使用經(jīng)驗(yàn)最豐富的人了,這也為了后來我協(xié)助別的使用了Hibernate的項(xiàng)目解決問題的原因。再次,我這次自己寫了 Framework,特別在批處理方面,運(yùn)用了許多剛學(xué)會(huì)理解掉的設(shè)計(jì)模式,這些模式讓我的程序更具有健壯性和可擴(kuò)展性,讓我在設(shè)計(jì)方面的能力大大提升 了。

          這次的編程競(jìng)賽我寫得比較認(rèn)真,代碼量的確也很大,總代碼行數(shù)超過了3萬(wàn)行,有效代碼行數(shù)也在1萬(wàn)行以上。經(jīng)過公司專家們的評(píng)定后,我 得到了第一名,雖然沒有什么獎(jiǎng)品,不過肯定了我這段時(shí)間以來的努力,我還是很開心的。而且這次的編程競(jìng)賽讓我大大增加了編碼的熟練度,而且也在其中演練了 許多自己想出來的編程技巧,為以后的發(fā)展帶來很大的好處。

              從4月份開始,公司由于比較清閑,所以部門內(nèi)部開始進(jìn)行各種培訓(xùn)。我們部門開展了3項(xiàng)培訓(xùn),第一項(xiàng)就是編程能力培訓(xùn),第二項(xiàng)是Oracle數(shù)據(jù)庫(kù)技術(shù)培 訓(xùn),第三項(xiàng)是測(cè)試技巧培訓(xùn)。在編程能力培訓(xùn)中,主要就是把原來沒有注意的細(xì)節(jié)采取大家討論,輪流講課的方式進(jìn)行的,雖然其中很多東西我原來都是知道的,不 過也有原來不清楚的地方。而且經(jīng)過了這次互相討論,更加加深了印象。在Oracle培訓(xùn)中我覺得收獲很大,這個(gè)Oracle培訓(xùn)采取了傳統(tǒng)的上課的模式, 由我們開發(fā)小組中一個(gè)取得OCM的老員工給我們講解。對(duì)于Oracle,我原來基本上就只會(huì)寫寫SQL語(yǔ)句,具體Oracle有什么特別的功能,可以做什 么我也不是很清楚。但是這次上課從Oracle的啟動(dòng)原理開始,讓我知道Oracle中究竟有什么,Oracle數(shù)據(jù)庫(kù)各部分在磁盤上是如何存放的, Control File,Redo File究竟是什么意思,在數(shù)據(jù)庫(kù)中起什么作用,數(shù)據(jù)庫(kù)是怎么依賴他們運(yùn)行的,還有如何對(duì)Oracle進(jìn)行系統(tǒng)管理員級(jí)別的管理,如何在不停止數(shù)據(jù)庫(kù)運(yùn)行 的情況下進(jìn)行數(shù)據(jù)庫(kù)的更新、升級(jí)、備份等等。這些東西雖然非常有用,但在平時(shí)的開發(fā)是學(xué)不到的,這次趁著這個(gè)機(jī)會(huì)大大提升了自己Oracle的水平,感覺 非常開心。數(shù)據(jù)庫(kù)一向是我的弱項(xiàng),在上大學(xué)的時(shí)候我SQL語(yǔ)句能力只是一般,數(shù)據(jù)庫(kù)管理配置什么基本一點(diǎn)都不懂,通過這次集中的培訓(xùn),我覺得自己的能力又 進(jìn)一步增強(qiáng)了,弱項(xiàng)也在慢慢退卻。在三項(xiàng)培訓(xùn)中最后進(jìn)行的測(cè)試培訓(xùn)我承認(rèn)我沒有怎么認(rèn)真去學(xué),所以學(xué)會(huì)的也就是些測(cè)試概念,具體的測(cè)試技巧什么的還是不怎 么會(huì)。現(xiàn)在開發(fā)和測(cè)試的結(jié)合性越來越高,看來要下下功夫,以免給淘汰咯。

              提了這段時(shí)間在公司的進(jìn)展,還沒說自己的學(xué)習(xí)呢,這段時(shí)間正好看見中文版的《JUnit in Action》出版了,在書的背后寫著“如果沒有看過這本書,就不要對(duì)J2EE進(jìn)行單元測(cè)試”這句話。我早在去年就了解了JUnit的強(qiáng)大功能,再加上 Ant的話對(duì)于回歸測(cè)試是非常便利的。趁有時(shí)間,我便于3月底4月初的時(shí)候開始看這本書。當(dāng)時(shí)的我看《精通EJB》第二版看了一半,發(fā)現(xiàn)其中錯(cuò)誤越來越 多,而且文字也有些地方不知所云了,所以扔下不再浪費(fèi)時(shí)間看那本書,專心攻讀《JUnit In Action》。憑良心說,Manning的這套In Action叢書的確很不錯(cuò),從我先前看的《Hibernate In Action》英文版就能看出來,其中對(duì)代碼的編排非常方便讀者,感覺可以很順利的看到你所想看到的代碼片斷。這套《JUnit In Action》也是一樣,博文視點(diǎn)的紙張還是很好的,排版使用了Manning的風(fēng)格,閱讀起來很舒服,所以我讀得很快,大概就兩個(gè)多星期就讀完了這本 400多頁(yè)的書。感覺的確收獲不淺,首先,原來的自動(dòng)化配置工具中只會(huì)使用一個(gè)Ant,其他的基本沒聽說過,在這本書上詳細(xì)介紹了Maven。聽過書中的 講解以及自己的試驗(yàn),的確覺得Maven功能很強(qiáng)大,不過感覺起來配置比Ant要麻煩,所以我自己的感覺是Ant在項(xiàng)目中還是會(huì)廣泛應(yīng)用,不過Maven 在大型項(xiàng)目,特別是整個(gè)Site中有很大的用武之地,對(duì)于我們來說,使用的方法都是很簡(jiǎn)單的,掌握如何編寫配置文件才是我們的關(guān)鍵。

          書對(duì) JUnit與Cactus在J2EE的測(cè)試手法上給了大量的事例,給人的感覺非常好,In Action這套叢書最大的優(yōu)點(diǎn)就在這里,用實(shí)例代碼片斷讓你迅速了解一樣?xùn)|西。在實(shí)際工作中其實(shí)JUnit應(yīng)用也是比較廣泛的,特別如果采取測(cè)試驅(qū)動(dòng)開 發(fā)的話,JUnit是必不可少的一部分。在TagLib測(cè)試,JSP單體測(cè)試,數(shù)據(jù)庫(kù)測(cè)試和EJB測(cè)試都是我以前根本沒有看過的東西。其實(shí)這次雖然學(xué)是學(xué) 會(huì)了,不過真正做的時(shí)候還是要有個(gè)代碼例子依葫蘆畫瓢。我想大家肯定也都有這種感覺,寫程序的時(shí)候先找一段有點(diǎn)相似的代碼片斷Copy過來,然后看看要修 改什么地方,真正從頭到尾自己用手寫的代碼片斷是不多的,除非你已經(jīng)爛熟于心。不過這本書快看完的時(shí)候,項(xiàng)目又來了。

              這次做一個(gè)企業(yè)的MIS系統(tǒng),與以往不同的是,這次客戶給了一個(gè)比較龐大的基盤,封裝了近100個(gè)Tag,基本上把各種各樣有可能遇到的操作都封裝到 Tag里面了。而且所有的畫面顯示等信息都是放在數(shù)據(jù)庫(kù)的Table中,所以這次要求不寫任何程序代碼,只是學(xué)會(huì)使用好這些Tag,然后利用這些Tag寫 出Jsp頁(yè)面。一開始的時(shí)候還真是頭疼,這些Tag一個(gè)都不明白,而且文檔不是非常齊全,Tag的Source中注釋也比較少,學(xué)習(xí)起來不是很方便。我們 一共3個(gè)人投入到這個(gè)項(xiàng)目的前期準(zhǔn)備中,在第一個(gè)星期的學(xué)習(xí)中大家互相分配好個(gè)人學(xué)習(xí)的模塊,隨時(shí)互相交流。在后來的深入中發(fā)現(xiàn)這個(gè)項(xiàng)目的業(yè)務(wù)邏輯操作會(huì) 使用PL/SQL以及存儲(chǔ)過程來進(jìn)行,對(duì)于我來說,PL/SQL是從來沒有做過的東西,就叫做一竅不通,于是我需要從頭開始學(xué)習(xí)PL/SQL,以及如何編 寫存儲(chǔ)過程。我從網(wǎng)上下了一個(gè)PL/SQL的電子書籍,然后在公司花了一天時(shí)間進(jìn)行學(xué)習(xí),個(gè)人用的是Toad來調(diào)試PL/SQL的,雖然別人喜歡用 PL/SQL Developer來進(jìn)行開發(fā),不過我還是比較鐘愛Toad,而且Toad的確功能也很強(qiáng)大,使用起來也很方便就是了。經(jīng)過第一天的PL/SQL的學(xué)習(xí), 基本掌握了全部語(yǔ)法以及存儲(chǔ)過程的書寫格式等等,開始能夠?qū)憣懛浅:?jiǎn)單的PL/SQL。接下來的兩三天不斷鞏固熟練,客戶那里也發(fā)過來幾本詳細(xì)設(shè)計(jì)讓我們 練習(xí)著做一下。有了實(shí)際的詳細(xì)設(shè)計(jì),再加上我們之間互相交流,我們提高的都很快,大概過了三四天,大家就把基本詳細(xì)設(shè)計(jì)代碼編寫完畢了,而且經(jīng)過實(shí)際鍛 煉,我的PL/SQL編寫存儲(chǔ)過程的水平也大大提升,已經(jīng)可以滿足開發(fā)中的需要了。

          這個(gè)項(xiàng)目因?yàn)槿绻覀円婚_始做的能讓客戶滿意的話,后續(xù) 的項(xiàng)目將會(huì)比較龐大,所以Leader決定把我們Group比較空閑的其他人也先培訓(xùn)一下,讓他們有點(diǎn)感覺,到以后正式開發(fā)的時(shí)候也能迅速進(jìn)入狀態(tài),負(fù)責(zé) 給他們培訓(xùn)的任務(wù)也就交給了我。說起來是培訓(xùn),其實(shí)也就是把大概流程以及方法通過一次會(huì)議的形式告訴他們,然后把我前面已經(jīng)作好的那個(gè)畫面作為他們的作 業(yè),要他們看著設(shè)計(jì)書自己把畫面制作出來。這個(gè)時(shí)候也要放勞動(dòng)節(jié)了,黃金周可以休息一個(gè)星期,想想就覺得很Happy。勞動(dòng)節(jié)的時(shí)候基本沒有怎么學(xué)習(xí),只 是先把XML-RPC仔細(xì)看了下,學(xué)會(huì)了如何去寫一個(gè)XML-RPC的應(yīng)用,接著稍微看了點(diǎn)SOAP,看得也不錯(cuò),只是些簡(jiǎn)單的SOAP的例子而已,那些 SOAP的復(fù)雜東西都沒有看。

              很快就五一黃金周七天放假放完,八號(hào)開始上班,上班后就開始正式做節(jié)前就定好的那個(gè)項(xiàng)目,這次性質(zhì)屬于試做,也就是人家先發(fā)一批設(shè)計(jì)書過來,我們?nèi)缓箝_始 Coding,大概做了一周后,我自己害了急性結(jié)膜炎,只能回家休息,這次可真的是只能休息了,眼睛覺得特別漲,不要說電腦了,連書都不能看,看了眼睛就 疼。所以在家就只能睡大覺,過了一周眼睛大概才復(fù)原,可以去公司上班了。回到公司以后,Leader通知我說我不用去做上次那個(gè)項(xiàng)目了,要我加入我們 Group的一個(gè)新的項(xiàng)目,這個(gè)項(xiàng)目比較大,當(dāng)時(shí)還處于東京剛剛做好基本設(shè)計(jì),我們從東京把任務(wù)接下來,準(zhǔn)備發(fā)回來做詳細(xì)設(shè)計(jì)。我進(jìn)去的時(shí)候項(xiàng)目才開始三 四天,基本上還沒有做什么,這次我進(jìn)入了詳細(xì)設(shè)計(jì)制作小組,開始進(jìn)行這個(gè)項(xiàng)目的詳細(xì)設(shè)計(jì)的制作。

              由于我屬于第一次在正式的項(xiàng)目中參與詳細(xì)設(shè)計(jì),所以很多東西都不明白,特別是業(yè)務(wù)上面的東西,許多日語(yǔ)中的業(yè)務(wù)術(shù)語(yǔ)我根本不明白,比如什么賣切,切替,仕 入什么的。看著基本設(shè)計(jì)書,感覺跟以前看詳細(xì)設(shè)計(jì)書有很大的不同。具體的東西寫的少了,業(yè)務(wù)流程邏輯框架什么的比較多,所以需要首先把業(yè)務(wù)內(nèi)容都熟悉了, 才可能寫出詳細(xì)設(shè)計(jì)來。這次的詳細(xì)設(shè)計(jì)我也不是孤軍奮戰(zhàn),而是有一個(gè)進(jìn)公司4年的老員工帶著我一起做,我的任務(wù)很輕,不過重點(diǎn)是學(xué)會(huì)如何去寫詳細(xì)設(shè)計(jì),也 許下次再有一個(gè)比較大的項(xiàng)目,就沒有別人再帶著我,而是我自己一個(gè)人去完成詳細(xì)設(shè)計(jì)了。大概詳細(xì)設(shè)計(jì)寫了20天左右,我被通知當(dāng)天把手上的一份詳細(xì)設(shè)計(jì)寫 完,第二天進(jìn)入方式設(shè)計(jì)小組進(jìn)行方式的設(shè)計(jì)。

          進(jìn)入方式小組以后,接到的任務(wù)就是好幾個(gè)編寫DB操作方面的代碼自動(dòng)化生成工具。由于這次DB 方面并沒有非常強(qiáng)制性的那種規(guī)約,所以SQL語(yǔ)句的編寫可以說比較隨意,這就給我工具的編寫帶來了很大的難度和挑戰(zhàn)。這次負(fù)責(zé)管理方式小組的人仍然是進(jìn)公 司以后經(jīng)常帶著我的那位技術(shù)專家,所以也真算很巧呢。寫工具其實(shí)很對(duì)自身代碼編寫的提高也很有好處,因?yàn)槭紫瓤蛻裟抢镔Y料會(huì)不斷修改,這些工具你為了以后 客戶更新資料后你能順利更新工具,你需要設(shè)計(jì)一個(gè)優(yōu)良的Framework,不一定需要多么復(fù)雜的Framework,不過一定要盡量把程序各方面的耦合 度盡量降低,這樣才有利于自己對(duì)工具進(jìn)行擴(kuò)展。緊接著很快,項(xiàng)目代碼編寫開始了,我的任務(wù)算中等偏上,有2個(gè)畫面和一個(gè)批處理需要編寫,復(fù)雜度還算比較繁 一點(diǎn)。這次項(xiàng)目需要編寫JUnit程序,每天都要進(jìn)行回歸測(cè)試,保證代碼Method的正確性。JUnit雖然自己會(huì)用,但是從來沒有在真正的項(xiàng)目中使 用,所以在真正用的時(shí)候感覺有點(diǎn)手足無(wú)措。以前做JUnit從來都是覺得給個(gè)參數(shù),檢測(cè)一個(gè)返回值就好了,其實(shí)不是那么回事,業(yè)務(wù)邏輯復(fù)雜了,自己需要做 大量的Stub來模擬真實(shí)的Class的返回值。設(shè)計(jì)一個(gè)好的Stub是比較困難的,特別在數(shù)據(jù)庫(kù)內(nèi)容比較豐富的時(shí)候,一張數(shù)據(jù)庫(kù)Table就有上百個(gè) 域,工作量可見一斑了。項(xiàng)目要到05年9月中旬才會(huì)結(jié)束,所以現(xiàn)在還在緊張的開發(fā)階段。我寫了JUnit的感覺就是難點(diǎn)不在如何去寫JUnit程序,而是 如何去設(shè)計(jì)測(cè)試用例。對(duì)于我們這樣不是以測(cè)試出身的程序員來說,設(shè)計(jì)測(cè)試用例是很痛苦而且很艱難的事情,估計(jì)有過相似經(jīng)驗(yàn)的人肯定會(huì)表示贊同。

              當(dāng)然我一邊在緊張的做項(xiàng)目,對(duì)于書本的學(xué)習(xí)也沒有閑著。這段時(shí)間抓緊把侯捷的Word排版藝術(shù)掃了一遍,看完覺得收獲頗豐。雖然我以前覺得我在Word上 用得挺不錯(cuò),日常的一些操作什么的我都會(huì),不過看這本書的中間我發(fā)現(xiàn)我還是有很多地方不會(huì)的,也學(xué)到了不少東西,在以后的Word排版中會(huì)很受到好處。由 于項(xiàng)目用到了Spring知識(shí),所以我也看了網(wǎng)絡(luò)上那個(gè)流傳廣泛的Spring開發(fā)指南的PDF看了一遍,感覺長(zhǎng)了見識(shí),對(duì)IOC以及DI有了進(jìn)一步的了 解,也理解了為什么需要采用IOC以及DI。不過這個(gè)也沒有深入下去仔細(xì)看,以后等項(xiàng)目稍微空閑一點(diǎn)的時(shí)候一定再把Hibernate和Spring好好 看一下,學(xué)習(xí)人家的設(shè)計(jì)理念,提高自己能力。對(duì)了,也許最重要的是我最近在看一本書,就是《J2EE核心模式》的第二版,我當(dāng)時(shí)原來準(zhǔn)備看電子版的這本 《Core J2EE Patterns》的,不過突然在書店發(fā)現(xiàn)這本書的中文版出來了,而且譯者有熊節(jié)的名字,也就是跟侯捷一起翻譯《重構(gòu)——改善既有代碼的設(shè)計(jì)》的那個(gè)譯 者,我比較相信他翻譯的水平,于是買回來看,雖然項(xiàng)目非常緊張,我一個(gè)月算上周末需要加班在100個(gè)小時(shí)左右的樣子,但是我相信時(shí)間是海綿里的水,只要去 擠,肯定會(huì)有的。所以我到現(xiàn)在大概看了2周的樣子,已經(jīng)看了300多頁(yè),而且感覺自己的設(shè)計(jì)視野也開闊了許多,這本書的確很好,把J2EE中常用的一些模 塊原理都說了出來,說明了為什么這么做好,這么做如何減少了耦合性,提高了可維護(hù)性等等,總之,有1年以上J2EE開發(fā)經(jīng)驗(yàn)而且覺得自己對(duì)J2EE有了比 較好的了解的開發(fā)人員我強(qiáng)烈推薦看這本書。看了這本書以后我都在回想以前設(shè)計(jì)的一些框架,一些模塊,覺得自己有很多地方當(dāng)時(shí)設(shè)計(jì)的時(shí)候覺得很精巧,不過卻 屬于弄巧成拙,加大了模塊的耦合性,所以在修改的時(shí)候比較難于下手。

          posted @ 2005-12-26 21:43 Dion 閱讀(2608) | 評(píng)論 (8)編輯 收藏

               摘要: 華為軟件編程規(guī)范和范例 document.title="華為軟件編程規(guī)范和范例 - "+document.title 目  錄1 排版62 注釋113 標(biāo)識(shí)符命名184 可讀性205 變量、結(jié)構(gòu)226 函數(shù)、過程287 可測(cè)性368 程序效率409 質(zhì)量保證4410 代碼編輯、編譯、審查5011 代碼測(cè)試、維護(hù)5212 宏531 排版11-1:程序塊要采...  閱讀全文
          posted @ 2005-12-20 08:59 Dion 閱讀(2990) | 評(píng)論 (3)編輯 收藏

               摘要: AOP@Work: 用 AspectJ 進(jìn)行性能監(jiān)視,第 2 部分通過裝載時(shí)織入使用Glassbox Inspector 文檔選項(xiàng) 將此頁(yè)作為電子郵件發(fā)送'); //--> 將此頁(yè)作為電子郵件發(fā)送未顯示需要 JavaScript 的文檔選項(xiàng)樣例代碼對(duì)此頁(yè)的評(píng)價(jià)幫助我們改進(jìn)這些內(nèi)容級(jí)別: 高級(jí)Ron Bodkin , 創(chuàng)始人, New Aspects of Software2005 年 ...  閱讀全文
          posted @ 2005-12-19 21:53 Dion 閱讀(1277) | 評(píng)論 (0)編輯 收藏

               摘要: AOP@Work: 用 AspectJ 進(jìn)行性能監(jiān)視,第 1 部分用 AspectJ 和 JMX 深入觀察 Glassbox Inspector文檔選項(xiàng) 將此頁(yè)作為電子郵件發(fā)送'); //--> 將此頁(yè)作為電子郵件發(fā)送未顯示需要 JavaScript 的文檔選項(xiàng)樣例代碼對(duì)此頁(yè)的評(píng)價(jià)幫助我們改進(jìn)這些內(nèi)容級(jí)別: 中級(jí)Ron Bodkin , 創(chuàng)始人, New Aspects of Softwa...  閱讀全文
          posted @ 2005-12-19 21:52 Dion 閱讀(1113) | 評(píng)論 (0)編輯 收藏

          1.如何獲得當(dāng)前文件路徑

          常用:

          字符串類型:System.getProperty("user.dir");

          綜合:

          package com.zcjl.test.base;
          import java.io.File;
          public class Test {
              public static void main(String[] args) throws Exception {
                  System.out.println(
                      Thread.currentThread().getContextClassLoader().getResource(""));
                  System.out.println(Test.class.getClassLoader().getResource(""));
                  System.out.println(ClassLoader.getSystemResource(""));
                  System.out.println(Test.class.getResource(""));
                  System.out.println(Test.class.getResource("/"));
                  System.out.println(new File("").getAbsolutePath());
                  System.out.println(System.getProperty("user.dir"));

              }
          }

          2.Web服務(wù)中

          (1).Weblogic

          WebApplication的系統(tǒng)文件根目錄是你的weblogic安裝所在根目錄。
          例如:如果你的weblogic安裝在c:\bea\weblogic700.....
          那么,你的文件根路徑就是c:\.
          所以,有兩種方式能夠讓你訪問你的服務(wù)器端的文件:
          a.使用絕對(duì)路徑:
          比如將你的參數(shù)文件放在c:\yourconfig\yourconf.properties,
          直接使用 new FileInputStream("yourconfig/yourconf.properties");
          b.使用相對(duì)路徑:
          相對(duì)路徑的根目錄就是你的webapplication的根路徑,即WEB-INF的上一級(jí)目錄,將你的參數(shù)文件放在yourwebapp\yourconfig\yourconf.properties,
          這樣使用:
          new FileInputStream("./yourconfig/yourconf.properties");
          這兩種方式均可,自己選擇。

          (2).Tomcat

          在類中輸出System.getProperty("user.dir");顯示的是%Tomcat_Home%/bin

          (3).Resin

          不是你的JSP放的相對(duì)路徑,是JSP引擎執(zhí)行這個(gè)JSP編譯成SERVLET
          的路徑為根.比如用新建文件法測(cè)試File f = new File("a.htm");
          這個(gè)a.htm在resin的安裝目錄下

          (4).如何讀相對(duì)路徑哪?

          在Java文件中g(shù)etResource或getResourceAsStream均可

          例:getClass().getResourceAsStream(filePath);//filePath可以是"/filename",這里的/代表web發(fā)布根路徑下WEB-INF/classes

          (5).獲得文件真實(shí)路徑

          string  file_real_path=request.getRealPath("mypath/filename"); 

          通常使用request.getRealPath("/"); 

          3.文件操作的類

          import java.io.*;
          import java.net.*;
          import java.util.*;
          //import javax.swing.filechooser.*;
          //import org.jr.swing.filter.*;

          /**
          * 此類中封裝一些常用的文件操作。
          * 所有方法都是靜態(tài)方法,不需要生成此類的實(shí)例,
          * 為避免生成此類的實(shí)例,構(gòu)造方法被申明為private類型的。
          * @since  0.1
          */

          public class FileUtil {
            /**
             * 私有構(gòu)造方法,防止類的實(shí)例化,因?yàn)楣ぞ哳惒恍枰獙?shí)例化。
             */
            private FileUtil() {

            }

            /**
             * 修改文件的最后訪問時(shí)間。
             * 如果文件不存在則創(chuàng)建該文件。
             * <b>目前這個(gè)方法的行為方式還不穩(wěn)定,主要是方法有些信息輸出,這些信息輸出是否保留還在考

          慮中。</b>
             * @param file 需要修改最后訪問時(shí)間的文件。
             * @since  0.1
             */
            public static void touch(File file) {
              long currentTime = System.currentTimeMillis();
              if (!file.exists()) {
                System.err.println("file not found:" + file.getName());
                System.err.println("Create a new file:" + file.getName());
                try {
                  if (file.createNewFile()) {
                  //  System.out.println("Succeeded!");
                  }
                  else {
                  //  System.err.println("Create file failed!");
                  }
                }
                catch (IOException e) {
                //  System.err.println("Create file failed!");
                  e.printStackTrace();
                }
              }
              boolean result = file.setLastModified(currentTime);
              if (!result) {
              //  System.err.println("touch failed: " + file.getName());
              }
            }

            /**
             * 修改文件的最后訪問時(shí)間。
             * 如果文件不存在則創(chuàng)建該文件。
             * <b>目前這個(gè)方法的行為方式還不穩(wěn)定,主要是方法有些信息輸出,這些信息輸出是否保留還在考

          慮中。</b>
             * @param fileName 需要修改最后訪問時(shí)間的文件的文件名。
             * @since  0.1
             */
            public static void touch(String fileName) {
              File file = new File(fileName);
              touch(file);
            }

            /**
             * 修改文件的最后訪問時(shí)間。
             * 如果文件不存在則創(chuàng)建該文件。
             * <b>目前這個(gè)方法的行為方式還不穩(wěn)定,主要是方法有些信息輸出,這些信息輸出是否保留還在考

          慮中。</b>
             * @param files 需要修改最后訪問時(shí)間的文件數(shù)組。
             * @since  0.1
             */
            public static void touch(File[] files) {
              for (int i = 0; i < files.length; i++) {
                touch(files);
              }
            }

            /**
             * 修改文件的最后訪問時(shí)間。
             * 如果文件不存在則創(chuàng)建該文件。
             * <b>目前這個(gè)方法的行為方式還不穩(wěn)定,主要是方法有些信息輸出,這些信息輸出是否保留還在考

          慮中。</b>
             * @param fileNames 需要修改最后訪問時(shí)間的文件名數(shù)組。
             * @since  0.1
             */
            public static void touch(String[] fileNames) {
              File[] files = new File[fileNames.length];
              for (int i = 0; i < fileNames.length; i++) {
                files = new File(fileNames);
              }
              touch(files);
            }

            /**
             * 判斷指定的文件是否存在。
             * @param fileName 要判斷的文件的文件名
             * @return 存在時(shí)返回true,否則返回false。
             * @since  0.1
             */
            public static boolean isFileExist(String fileName) {
              return new File(fileName).isFile();
            }

            /**
             * 創(chuàng)建指定的目錄。
             * 如果指定的目錄的父目錄不存在則創(chuàng)建其目錄書上所有需要的父目錄。
             * <b>注意:可能會(huì)在返回false的時(shí)候創(chuàng)建部分父目錄。</b>
             * @param file 要?jiǎng)?chuàng)建的目錄
             * @return 完全創(chuàng)建成功時(shí)返回true,否則返回false。
             * @since  0.1
             */
            public static boolean makeDirectory(File file) {
              File parent = file.getParentFile();
              if (parent != null) {
                return parent.mkdirs();
              }
              return false;
            }

            /**
             * 創(chuàng)建指定的目錄。
             * 如果指定的目錄的父目錄不存在則創(chuàng)建其目錄書上所有需要的父目錄。
             * <b>注意:可能會(huì)在返回false的時(shí)候創(chuàng)建部分父目錄。</b>
             * @param fileName 要?jiǎng)?chuàng)建的目錄的目錄名
             * @return 完全創(chuàng)建成功時(shí)返回true,否則返回false。
             * @since  0.1
             */
            public static boolean makeDirectory(String fileName) {
              File file = new File(fileName);
              return makeDirectory(file);
            }

            /**
             * 清空指定目錄中的文件。
             * 這個(gè)方法將盡可能刪除所有的文件,但是只要有一個(gè)文件沒有被刪除都會(huì)返回false。
             * 另外這個(gè)方法不會(huì)迭代刪除,即不會(huì)刪除子目錄及其內(nèi)容。
             * @param directory 要清空的目錄
             * @return 目錄下的所有文件都被成功刪除時(shí)返回true,否則返回false.
             * @since  0.1
             */
            public static boolean emptyDirectory(File directory) {
              boolean result = false;
              File[] entries = directory.listFiles();
              for (int i = 0; i < entries.length; i++) {
                if (!entries.delete()) {
                  result = false;
                }
              }
              return true;
            }

            /**
             * 清空指定目錄中的文件。
             * 這個(gè)方法將盡可能刪除所有的文件,但是只要有一個(gè)文件沒有被刪除都會(huì)返回false。
             * 另外這個(gè)方法不會(huì)迭代刪除,即不會(huì)刪除子目錄及其內(nèi)容。
             * @param directoryName 要清空的目錄的目錄名
             * @return 目錄下的所有文件都被成功刪除時(shí)返回true,否則返回false。
             * @since  0.1
             */
            public static boolean emptyDirectory(String directoryName) {
              File dir = new File(directoryName);
              return emptyDirectory(dir);
            }

            /**
             * 刪除指定目錄及其中的所有內(nèi)容。
             * @param dirName 要?jiǎng)h除的目錄的目錄名
             * @return 刪除成功時(shí)返回true,否則返回false。
             * @since  0.1
             */
            public static boolean deleteDirectory(String dirName) {
              return deleteDirectory(new File(dirName));
            }

            /**
             * 刪除指定目錄及其中的所有內(nèi)容。
             * @param dir 要?jiǎng)h除的目錄
             * @return 刪除成功時(shí)返回true,否則返回false。
             * @since  0.1
             */
            public static boolean deleteDirectory(File dir) {
              if ( (dir == null) || !dir.isDirectory()) {
                throw new IllegalArgumentException("Argument " + dir +
                                                   " is not a directory. ");
              }

              File[] entries = dir.listFiles();
              int sz = entries.length;

              for (int i = 0; i < sz; i++) {
                if (entries.isDirectory()) {
                  if (!deleteDirectory(entries)) {
                    return false;
                  }
                }
                else {
                  if (!entries.delete()) {
                    return false;
                  }
                }
              }

              if (!dir.delete()) {
                return false;
              }
              return true;
            }


            /**
             * 返回文件的URL地址。
             * @param file 文件
             * @return 文件對(duì)應(yīng)的的URL地址
             * @throws MalformedURLException
             * @since  0.4
             * @deprecated 在實(shí)現(xiàn)的時(shí)候沒有注意到File類本身帶一個(gè)toURL方法將文件路徑轉(zhuǎn)換為URL。
             *             請(qǐng)使用File.toURL方法。
             */
            public static URL getURL(File file) throws MalformedURLException {
              String fileURL = "file:/" + file.getAbsolutePath();
              URL url = new URL(fileURL);
              return url;
            }

            /**
             * 從文件路徑得到文件名。
             * @param filePath 文件的路徑,可以是相對(duì)路徑也可以是絕對(duì)路徑
             * @return 對(duì)應(yīng)的文件名
             * @since  0.4
             */
            public static String getFileName(String filePath) {
              File file = new File(filePath);
              return file.getName();
            }

            /**
             * 從文件名得到文件絕對(duì)路徑。
             * @param fileName 文件名
             * @return 對(duì)應(yīng)的文件路徑
             * @since  0.4
             */
            public static String getFilePath(String fileName) {
              File file = new File(fileName);
              return file.getAbsolutePath();
            }

            /**
             * 將DOS/Windows格式的路徑轉(zhuǎn)換為UNIX/Linux格式的路徑。
             * 其實(shí)就是將路徑中的"\"全部換為"/",因?yàn)樵谀承┣闆r下我們轉(zhuǎn)換為這種方式比較方便,
             * 某中程度上說"/"比"\"更適合作為路徑分隔符,而且DOS/Windows也將它當(dāng)作路徑分隔符。
             * @param filePath 轉(zhuǎn)換前的路徑
             * @return 轉(zhuǎn)換后的路徑
             * @since  0.4
             */
            public static String toUNIXpath(String filePath) {
              return filePath.replace('\\', '/');
            }

            /**
             * 從文件名得到UNIX風(fēng)格的文件絕對(duì)路徑。
             * @param fileName 文件名
             * @return 對(duì)應(yīng)的UNIX風(fēng)格的文件路徑
             * @since  0.4
             * @see #toUNIXpath(String filePath) toUNIXpath
             */
            public static String getUNIXfilePath(String fileName) {
              File file = new File(fileName);
              return toUNIXpath(file.getAbsolutePath());
            }

            /**
             * 得到文件的類型。
             * 實(shí)際上就是得到文件名中最后一個(gè)“.”后面的部分。
             * @param fileName 文件名
             * @return 文件名中的類型部分
             * @since  0.5
             */
            public static String getTypePart(String fileName) {
              int point = fileName.lastIndexOf('.');
              int length = fileName.length();
              if (point == -1 || point == length - 1) {
                return "";
              }
              else {
                return fileName.substring(point + 1, length);
              }
            }

            /**
             * 得到文件的類型。
             * 實(shí)際上就是得到文件名中最后一個(gè)“.”后面的部分。
             * @param file 文件
             * @return 文件名中的類型部分
             * @since  0.5
             */
            public static String getFileType(File file) {
              return getTypePart(file.getName());
            }

            /**
             * 得到文件的名字部分。
             * 實(shí)際上就是路徑中的最后一個(gè)路徑分隔符后的部分。
             * @param fileName 文件名
             * @return 文件名中的名字部分
             * @since  0.5
             */
            public static String getNamePart(String fileName) {
              int point = getPathLsatIndex(fileName);
              int length = fileName.length();
              if (point == -1) {
                return fileName;
              }
              else if (point == length - 1) {
                int secondPoint = getPathLsatIndex(fileName, point - 1);
                if (secondPoint == -1) {
                  if (length == 1) {
                    return fileName;
                  }
                  else {
                    return fileName.substring(0, point);
                  }
                }
                else {
                  return fileName.substring(secondPoint + 1, point);
                }
              }
              else {
                return fileName.substring(point + 1);
              }
            }

            /**
             * 得到文件名中的父路徑部分。
             * 對(duì)兩種路徑分隔符都有效。
             * 不存在時(shí)返回""。
             * 如果文件名是以路徑分隔符結(jié)尾的則不考慮該分隔符,例如"/path/"返回""。
             * @param fileName 文件名
             * @return 父路徑,不存在或者已經(jīng)是父目錄時(shí)返回""
             * @since  0.5
             */
            public static String getPathPart(String fileName) {
              int point = getPathLsatIndex(fileName);
              int length = fileName.length();
              if (point == -1) {
                return "";
              }
              else if (point == length - 1) {
                int secondPoint = getPathLsatIndex(fileName, point - 1);
                if (secondPoint == -1) {
                  return "";
                }
                else {
                  return fileName.substring(0, secondPoint);
                }
              }
              else {
                return fileName.substring(0, point);
              }
            }

            /**
             * 得到路徑分隔符在文件路徑中首次出現(xiàn)的位置。
             * 對(duì)于DOS或者UNIX風(fēng)格的分隔符都可以。
             * @param fileName 文件路徑
             * @return 路徑分隔符在路徑中首次出現(xiàn)的位置,沒有出現(xiàn)時(shí)返回-1。
             * @since  0.5
             */
            public static int getPathIndex(String fileName) {
              int point = fileName.indexOf('/');
              if (point == -1) {
                point = fileName.indexOf('\\');
              }
              return point;
            }

            /**
             * 得到路徑分隔符在文件路徑中指定位置后首次出現(xiàn)的位置。
             * 對(duì)于DOS或者UNIX風(fēng)格的分隔符都可以。
             * @param fileName 文件路徑
             * @param fromIndex 開始查找的位置
             * @return 路徑分隔符在路徑中指定位置后首次出現(xiàn)的位置,沒有出現(xiàn)時(shí)返回-1。
             * @since  0.5
             */
            public static int getPathIndex(String fileName, int fromIndex) {
              int point = fileName.indexOf('/', fromIndex);
              if (point == -1) {
                point = fileName.indexOf('\\', fromIndex);
              }
              return point;
            }

            /**
             * 得到路徑分隔符在文件路徑中最后出現(xiàn)的位置。
             * 對(duì)于DOS或者UNIX風(fēng)格的分隔符都可以。
             * @param fileName 文件路徑
             * @return 路徑分隔符在路徑中最后出現(xiàn)的位置,沒有出現(xiàn)時(shí)返回-1。
             * @since  0.5
             */
            public static int getPathLsatIndex(String fileName) {
              int point = fileName.lastIndexOf('/');
              if (point == -1) {
                point = fileName.lastIndexOf('\\');
              }
              return point;
            }

            /**
             * 得到路徑分隔符在文件路徑中指定位置前最后出現(xiàn)的位置。
             * 對(duì)于DOS或者UNIX風(fēng)格的分隔符都可以。
             * @param fileName 文件路徑
             * @param fromIndex 開始查找的位置
             * @return 路徑分隔符在路徑中指定位置前最后出現(xiàn)的位置,沒有出現(xiàn)時(shí)返回-1。
             * @since  0.5
             */
            public static int getPathLsatIndex(String fileName, int fromIndex) {
              int point = fileName.lastIndexOf('/', fromIndex);
              if (point == -1) {
                point = fileName.lastIndexOf('\\', fromIndex);
              }
              return point;
            }

            /**
             * 將文件名中的類型部分去掉。
             * @param filename 文件名
             * @return 去掉類型部分的結(jié)果
             * @since  0.5
             */
            public static String trimType(String filename) {
              int index = filename.lastIndexOf(".");
              if (index != -1) {
                return filename.substring(0, index);
              }
              else {
                return filename;
              }
            }
            /**
             * 得到相對(duì)路徑。
             * 文件名不是目錄名的子節(jié)點(diǎn)時(shí)返回文件名。
             * @param pathName 目錄名
             * @param fileName 文件名
             * @return 得到文件名相對(duì)于目錄名的相對(duì)路徑,目錄下不存在該文件時(shí)返回文件名
             * @since  0.5
             */
            public static String getSubpath(String pathName,String fileName) {
              int index = fileName.indexOf(pathName);
              if (index != -1) {
                return fileName.substring(index + pathName.length() + 1);
              }
              else {
                return fileName;
              }
            }

          }
           4.遺留問題

          目前new FileInputStream()只會(huì)使用絕對(duì)路徑,相對(duì)沒用過,因?yàn)橐鄬?duì)于web服務(wù)器地址,比較麻煩

          還不如寫個(gè)配置文件來的快哪

          5.按Java文件類型分類讀取配置文件

          配 置文件是應(yīng)用系統(tǒng)中不可缺少的,可以增加程序的靈活性。java.util.Properties是從jdk1.2就有的類,一直到現(xiàn)在都支持load ()方法,jdk1.4以后save(output,string) ->store(output,string)。如果只是單純的讀,根本不存在煩惱的問題。web層可以通過 Thread.currentThread().getContextClassLoader().
          getResourceAsStream("xx.properties") 獲取;Application可以通過new FileInputStream("xx.properties");直接在classes一級(jí)獲取。關(guān)鍵是有時(shí)我們需要通過web修改配置文件,我們不 能將路徑寫死了。經(jīng)過測(cè)試覺得有以下心得:

          1.servlet中讀寫。如果運(yùn)用Struts 或者Servlet可以直接在初始化參數(shù)中配置,調(diào)用時(shí)根據(jù)servlet的getRealPath("/")獲取真實(shí)路徑,再根據(jù)String file = this.servlet.getInitParameter("abc");獲取相對(duì)的WEB-INF的相對(duì)路徑。
          例:
          InputStream input = Thread.currentThread().getContextClassLoader().
          getResourceAsStream("abc.properties");
          Properties prop = new Properties();
          prop.load(input);
          input.close();
          OutputStream out = new FileOutputStream(path);
          prop.setProperty("abc", “test");
          prop.store(out, “–test–");
          out.close();

          2.直接在jsp中操作,通過jsp內(nèi)置對(duì)象獲取可操作的絕對(duì)地址。
          例:
          // jsp頁(yè)面
          String path = pageContext.getServletContext().getRealPath("/");
          String realPath = path+"/WEB-INF/classes/abc.properties";

          //java 程序
          InputStream in = getClass().getClassLoader().getResourceAsStream("abc.properties"); // abc.properties放在webroot/WEB-INF/classes/目錄下
          prop.load(in);
          in.close();

          OutputStream out = new FileOutputStream(path); // path為通過頁(yè)面?zhèn)魅氲穆窂?br>prop.setProperty("abc", “abcccccc");
          prop.store(out, “–test–");
          out.close();

          3.只通過Java程序操作資源文件
          InputStream in = new FileInputStream("abc.properties"); // 放在classes同級(jí)

          OutputStream out = new FileOutputStream("abc.properties");

          posted @ 2005-12-16 22:53 Dion 閱讀(31518) | 評(píng)論 (8)編輯 收藏

          六種異常處理的陋習(xí)

          你覺得自己是一個(gè)Java專家嗎?是否肯定自己已經(jīng)全面掌握了Java的異常處理機(jī)制?在下面這段代碼中,你能夠迅速找出異常處理的六個(gè)問題嗎?

          1 OutputStreamWriter out = ...
          2 java.sql.Connection conn = ...
          3 try { // ⑸
          4  Statement stat = conn.createStatement();
          5  ResultSet rs = stat.executeQuery(
          6   "select uid, name from user");
          7  while (rs.next())
          8  {
          9   out.println("ID:" + rs.getString("uid") // ⑹
          10    ",姓名:" + rs.getString("name"));
          11  }
          12  conn.close(); // ⑶
          13  out.close();
          14 }
          15 catch(Exception ex) // ⑵
          16 {
          17  ex.printStackTrace(); //⑴,⑷
          18 }


            作為一個(gè)Java程序員,你至少應(yīng)該能夠找出兩個(gè)問題。但是,如果你不能找出全部六個(gè)問題,請(qǐng)繼續(xù)閱讀本文。

            本文討論的不是Java異常處理的一般性原則,因?yàn)檫@些原則已經(jīng)被大多數(shù)人熟知。我們要做的是分析各種可稱為“反例”(anti-pattern)的違背優(yōu)秀編碼規(guī)范的常見壞習(xí)慣,幫助讀者熟悉這些典型的反面例子,從而能夠在實(shí)際工作中敏銳地察覺和避免這些問題。

            反例之一:丟棄異常

            代碼:15行-18行。

             這段代碼捕獲了異常卻不作任何處理,可以算得上Java編程中的殺手。從問題出現(xiàn)的頻繁程度和禍害程度來看,它也許可以和C/C++程序的一個(gè)惡名遠(yuǎn)播 的問題相提并論??不檢查緩沖區(qū)是否已滿。如果你看到了這種丟棄(而不是拋出)異常的情況,可以百分之九十九地肯定代碼存在問題(在極少數(shù)情況下,這段代 碼有存在的理由,但最好加上完整的注釋,以免引起別人誤解)。

            這段代碼的錯(cuò)誤在于,異常(幾乎)總是意味著某些事情不對(duì)勁了,或 者說至少發(fā)生了某些不尋常的事情,我們不應(yīng)該對(duì)程序發(fā)出的求救信號(hào)保持沉默和無(wú)動(dòng)于衷。調(diào)用一下printStackTrace算不上“處理異常”。不 錯(cuò),調(diào)用printStackTrace對(duì)調(diào)試程序有幫助,但程序調(diào)試階段結(jié)束之后,printStackTrace就不應(yīng)再在異常處理模塊中擔(dān)負(fù)主要責(zé) 任了。

            丟棄異常的情形非常普遍。打開JDK的ThreadDeath類的文檔,可以看到下面這段說明:“特別地,雖然出現(xiàn) ThreadDeath是一種‘正常的情形’,但ThreadDeath類是Error而不是Exception的子類,因?yàn)樵S多應(yīng)用會(huì)捕獲所有的 Exception然后丟棄它不再理睬。”這段話的意思是,雖然ThreadDeath代表的是一種普通的問題,但鑒于許多應(yīng)用會(huì)試圖捕獲所有異常然后不 予以適當(dāng)?shù)奶幚恚訨DK把ThreadDeath定義成了Error的子類,因?yàn)镋rror類代表的是一般的應(yīng)用不應(yīng)該去捕獲的嚴(yán)重問題。可見,丟棄 異常這一壞習(xí)慣是如此常見,它甚至已經(jīng)影響到了Java本身的設(shè)計(jì)。

            那么,應(yīng)該怎樣改正呢?主要有四個(gè)選擇:

            1、處理異常。針對(duì)該異常采取一些行動(dòng),例如修正問題、提醒某個(gè)人或進(jìn)行其他一些處理,要根據(jù)具體的情形確定應(yīng)該采取的動(dòng)作。再次說明,調(diào)用printStackTrace算不上已經(jīng)“處理好了異常”。

            2、重新拋出異常。處理異常的代碼在分析異常之后,認(rèn)為自己不能處理它,重新拋出異常也不失為一種選擇。

            3、把該異常轉(zhuǎn)換成另一種異常。大多數(shù)情況下,這是指把一個(gè)低級(jí)的異常轉(zhuǎn)換成應(yīng)用級(jí)的異常(其含義更容易被用戶了解的異常)。

            4、不要捕獲異常。

            結(jié)論一:既然捕獲了異常,就要對(duì)它進(jìn)行適當(dāng)?shù)奶幚怼2灰东@異常之后又把它丟棄,不予理睬。

            反例之二:不指定具體的異常

            代碼:15行。

            許多時(shí)候人們會(huì)被這樣一種“美妙的”想法吸引:用一個(gè)catch語(yǔ)句捕獲所有的異常。最常見的情形就是使用catch(Exception ex)語(yǔ)句。但實(shí)際上,在絕大多數(shù)情況下,這種做法不值得提倡。為什么呢?

             要理解其原因,我們必須回顧一下catch語(yǔ)句的用途。catch語(yǔ)句表示我們預(yù)期會(huì)出現(xiàn)某種異常,而且希望能夠處理該異常。異常類的作用就是告訴 Java編譯器我們想要處理的是哪一種異常。由于絕大多數(shù)異常都直接或間接從java.lang.Exception派生,catch (Exception ex)就相當(dāng)于說我們想要處理幾乎所有的異常。

            再來看看前面的代碼例子。我們真正想要捕獲的異常是什么 呢?最明顯的一個(gè)是SQLException,這是JDBC操作中常見的異常。另一個(gè)可能的異常是IOException,因?yàn)樗僮? OutputStreamWriter。顯然,在同一個(gè)catch塊中處理這兩種截然不同的異常是不合適的。如果用兩個(gè)catch塊分別捕獲 SQLException和IOException就要好多了。這就是說,catch語(yǔ)句應(yīng)當(dāng)盡量指定具體的異常類型,而不應(yīng)該指定涵蓋范圍太廣的 Exception類。

            另一方面,除了這兩個(gè)特定的異常,還有其他許多異常也可能出現(xiàn)。例如,如果由于某種原因, executeQuery返回了null,該怎么辦?答案是讓它們繼續(xù)拋出,即不必捕獲也不必處理。實(shí)際上,我們不能也不應(yīng)該去捕獲可能出現(xiàn)的所有異常, 程序的其他地方還有捕獲異常的機(jī)會(huì)??直至最后由JVM處理。

            結(jié)論二:在catch語(yǔ)句中盡可能指定具體的異常類型,必要時(shí)使用多個(gè)catch。不要試圖處理所有可能出現(xiàn)的異常。

            反例之三:占用資源不釋放

            代碼:3行-14行。

            異常改變了程序正常的執(zhí)行流程。這個(gè)道理雖然簡(jiǎn)單,卻常常被人們忽視。如果程序用到了文件、Socket、JDBC連接之類的資源,即使遇到了異常,也要正確釋放占用的資源。為此,Java提供了一個(gè)簡(jiǎn)化這類操作的關(guān)鍵詞finally。

            finally是樣好東西:不管是否出現(xiàn)了異常,F(xiàn)inally保證在try/catch/finally塊結(jié)束之前,執(zhí)行清理任務(wù)的代碼總是有機(jī)會(huì)執(zhí)行。遺憾的是有些人卻不習(xí)慣使用finally。

            當(dāng)然,編寫finally塊應(yīng)當(dāng)多加小心,特別是要注意在finally塊之內(nèi)拋出的異常??這是執(zhí)行清理任務(wù)的最后機(jī)會(huì),盡量不要再有難以處理的錯(cuò)誤。

            結(jié)論三:保證所有資源都被正確釋放。充分運(yùn)用finally關(guān)鍵詞。

          反例之四:不說明異常的詳細(xì)信息

            代碼:3行-18行。

            仔細(xì)觀察這段代碼:如果循環(huán)內(nèi)部出現(xiàn)了異常,會(huì)發(fā)生什么事情?我們可以得到足夠的信息判斷循環(huán)內(nèi)部出錯(cuò)的原因嗎?不能。我們只能知道當(dāng)前正在處理的類發(fā)生了某種錯(cuò)誤,但卻不能獲得任何信息判斷導(dǎo)致當(dāng)前錯(cuò)誤的原因。

            printStackTrace的堆棧跟蹤功能顯示出程序運(yùn)行到當(dāng)前類的執(zhí)行流程,但只提供了一些最基本的信息,未能說明實(shí)際導(dǎo)致錯(cuò)誤的原因,同時(shí)也不易解讀。

            因此,在出現(xiàn)異常時(shí),最好能夠提供一些文字信息,例如當(dāng)前正在執(zhí)行的類、方法和其他狀態(tài)信息,包括以一種更適合閱讀的方式整理和組織printStackTrace提供的信息。

            結(jié)論四:在異常處理模塊中提供適量的錯(cuò)誤原因信息,組織錯(cuò)誤信息使其易于理解和閱讀。

            反例之五:過于龐大的try塊

            代碼:3行-14行。

             經(jīng)常可以看到有人把大量的代碼放入單個(gè)try塊,實(shí)際上這不是好習(xí)慣。這種現(xiàn)象之所以常見,原因就在于有些人圖省事,不愿花時(shí)間分析一大塊代碼中哪幾行 代碼會(huì)拋出異常、異常的具體類型是什么。把大量的語(yǔ)句裝入單個(gè)巨大的try塊就象是出門旅游時(shí)把所有日常用品塞入一個(gè)大箱子,雖然東西是帶上了,但要找出 來可不容易。

            一些新手常常把大量的代碼放入單個(gè)try塊,然后再在catch語(yǔ)句中聲明Exception,而不是分離各個(gè)可能出現(xiàn)異常的段落并分別捕獲其異常。這種做法為分析程序拋出異常的原因帶來了困難,因?yàn)橐淮蠖未a中有太多的地方可能拋出Exception。

            結(jié)論五:盡量減小try塊的體積。

            反例之六:輸出數(shù)據(jù)不完整

            代碼:7行-11行。

             不完整的數(shù)據(jù)是Java程序的隱形殺手。仔細(xì)觀察這段代碼,考慮一下如果循環(huán)的中間拋出了異常,會(huì)發(fā)生什么事情。循環(huán)的執(zhí)行當(dāng)然是要被打斷的,其次, catch塊會(huì)執(zhí)行??就這些,再也沒有其他動(dòng)作了。已經(jīng)輸出的數(shù)據(jù)怎么辦?使用這些數(shù)據(jù)的人或設(shè)備將收到一份不完整的(因而也是錯(cuò)誤的)數(shù)據(jù),卻得不到 任何有關(guān)這份數(shù)據(jù)是否完整的提示。對(duì)于有些系統(tǒng)來說,數(shù)據(jù)不完整可能比系統(tǒng)停止運(yùn)行帶來更大的損失。

            較為理想的處置辦法是向輸出設(shè)備寫一些信息,聲明數(shù)據(jù)的不完整性;另一種可能有效的辦法是,先緩沖要輸出的數(shù)據(jù),準(zhǔn)備好全部數(shù)據(jù)之后再一次性輸出。

            結(jié)論六:全面考慮可能出現(xiàn)的異常以及這些異常對(duì)執(zhí)行流程的影響。

            改寫后的代碼

            根據(jù)上面的討論,下面給出改寫后的代碼。也許有人會(huì)說它稍微有點(diǎn)?嗦,但是它有了比較完備的異常處理機(jī)制。

          OutputStreamWriter out = ...
          java.sql.Connection conn = ...
          try {
           Statement stat = conn.createStatement();
           ResultSet rs = stat.executeQuery(
            "select uid, name from user");
           while (rs.next())
           {
            out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name"));
           }
          }
          catch(SQLException sqlex)
          {
           out.println("警告:數(shù)據(jù)不完整");
           throw new ApplicationException("讀取數(shù)據(jù)時(shí)出現(xiàn)SQL錯(cuò)誤", sqlex);
          }
          catch(IOException ioex)
          {
           throw new ApplicationException("寫入數(shù)據(jù)時(shí)出現(xiàn)IO錯(cuò)誤", ioex);
          }
          finally
          {
           if (conn != null) {
            try {
             conn.close();
            }
            catch(SQLException sqlex2)
            {
             System.err(this.getClass().getName() + ".mymethod - 不能關(guān)閉數(shù)據(jù)庫(kù)連接: " + sqlex2.toString());
            }
           }

           if (out != null) {
            try {
             out.close();
            }
            catch(IOException ioex2)
            {
             System.err(this.getClass().getName() + ".mymethod - 不能關(guān)閉輸出文件" + ioex2.toString());
            }
           }
          }

            本文的結(jié)論不是放之四海皆準(zhǔn)的教條,有時(shí)常識(shí)和經(jīng)驗(yàn)才是最好的老師。如果你對(duì)自己的做法沒有百分之百的信心,務(wù)必加上詳細(xì)、全面的注釋。

             另一方面,不要笑話這些錯(cuò)誤,不妨問問你自己是否真地徹底擺脫了這些壞習(xí)慣。即使最有經(jīng)驗(yàn)的程序員偶爾也會(huì)誤入歧途,原因很簡(jiǎn)單,因?yàn)樗鼈兇_確實(shí)實(shí)帶來 了“方便”。所有這些反例都可以看作Java編程世界的惡魔,它們美麗動(dòng)人,無(wú)孔不入,時(shí)刻誘惑著你。也許有人會(huì)認(rèn)為這些都屬于雞皮蒜毛的小事,不足掛 齒,但請(qǐng)記住:勿以惡小而為之,勿以善小而不為。



          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=553341

          posted @ 2005-12-16 22:52 Dion 閱讀(674) | 評(píng)論 (1)編輯 收藏

          在SPRING中實(shí)現(xiàn)事務(wù)暫停

          作者:Juergen Hoeller

          譯者:xMatrix





          版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
          作者:Juergen Hoeller;xMatrix
          原文地址:http://dev2dev.bea.com/pub/a/2005/07/spring_transactions.html
          中文地址:http://www.matrix.org.cn/resource/article/44/44054_Transaction+Spring.html
          關(guān)鍵詞: Transaction Suspension Spring

          摘要

          Spring 框架是一個(gè)流行的基于輕量級(jí)控制反轉(zhuǎn)容器的Java/J2EE應(yīng)用框架,尤其在數(shù)據(jù)訪問和事務(wù)管理方面的能力是眾所周知的。Spring的聲明性事務(wù)分離 可以應(yīng)用到任何POJO目標(biāo)對(duì)象,并且包含所有EJB基于容器管理事務(wù)中的已聲明事務(wù)。后臺(tái)的事務(wù)管理器支持簡(jiǎn)單的基于JDBC的事務(wù)和全功能的基于 JTA的J2EE事務(wù)。

          這篇文章詳細(xì)的討論了Spring的事務(wù)管理特性。重點(diǎn)是如何在使用JTA作為后臺(tái)事務(wù)策略的基礎(chǔ)上讓POJO利 用Spring的聲明性事務(wù),這也顯示了Spring的事務(wù)服務(wù)可以無(wú)縫地與J2EE服務(wù)器(如BEA WebLogic Server的事務(wù)協(xié)調(diào)器)的事務(wù)協(xié)調(diào)器進(jìn)行交互,作為EJB CMT傳統(tǒng)事務(wù)分離方式的一個(gè)替代者。

          POJO的聲明性事務(wù)

          作為Spring聲明性事務(wù)分離方式的樣例,讓我們來看一下Spring的樣例應(yīng)用PetClinic的中心服務(wù)外觀中的配置:
          清單1:
          <bean id="dataSource" 
             class="org.springframework.jndi.JndiObjectFactoryBean">
               <property name="jndiName">
                  <value>java:comp/env/jdbc/petclinic</value>
               </property>
          </bean>

          <bean id="transactionManager"
             class="org.springframework.transaction.jta.JtaTransactionManager"/>

          <bean id="clinicTarget"
             class="org.springframework.samples.petclinic.jdbc.JdbcClinic">
              <property name="dataSource"><ref bean="dataSource"/></property>
          </bean>

          <bean id="clinic"
             class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
              <property name="transactionManager"><ref bean="transactionManager"/></property>
              <property name="target"><ref bean="clinicTarget"/></property>
              <property name="transactionAttributes">
                  <props>
                      <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
                      <prop key="store*">PROPAGATION_REQUIRED</prop>
                  </props>
              </property>
          </bean>


          他遵循Spring的標(biāo)準(zhǔn)XMLBean定義格式。定義了:
          1、一個(gè)DataSource引用,指向一個(gè)JNDI位置—在J2EE服務(wù)器管理下這將從JNDI環(huán)境中獲取特定的DataSource。
          2、一個(gè)應(yīng)用服務(wù)實(shí)現(xiàn)—這是一個(gè)POJO,封裝了業(yè)務(wù)和數(shù)據(jù)訪問邏輯。在這里實(shí)現(xiàn)了應(yīng)用中的Clinic服務(wù)接口。
          3、一個(gè)應(yīng)用服務(wù)的事務(wù)代理—這個(gè)代理為目標(biāo)服務(wù)定義了事務(wù)屬性,匹配特定的方法名模式并為之創(chuàng)建相應(yīng)的事務(wù)。在實(shí)際的事務(wù)管理中,代理指向一個(gè)PlatformTransactionManager實(shí)現(xiàn)。
          注意:除了顯式的代理定義,Spring還支持自動(dòng)代理機(jī)制和通過Commons Attributes或J2SE 5.0注解實(shí)現(xiàn)源程序級(jí)的元數(shù)據(jù)使用。這些可選方法的討論超過了本文的范圍。可以參考Spring的文檔來了解相關(guān)細(xì)節(jié)。


          業(yè)務(wù)接口和業(yè)務(wù)實(shí)現(xiàn)是特定于應(yīng)用的并且不需要關(guān)心Spring或者Spring的事務(wù)管理。普通Java對(duì)象可以作為服務(wù)的目標(biāo)對(duì)象,而且任何普通Java接口可以作為服務(wù)的接口。下面是一個(gè)Clinic接口的示例:
          清單2:
          public interface Clinic {
              Pet loadPet(int id);
              void storePet(Pet pet);
              ...
          }



          這個(gè)接口的實(shí)現(xiàn)如下顯示,假設(shè)他使用JDBC來執(zhí)行必要的數(shù)據(jù)訪問。他通過bean屬性的設(shè)置方法來獲取JDBC的DataSource;這與上面的配置中的dataSource屬性定義相對(duì)應(yīng)。
          清單3:
          public class JdbcClinic implements Clinic {

              private DataSource dataSource;

              public void setDataSource(DataSource dataSource) {
                this.dataSource = dataSource;
              }

              public Pet loadPet(int id) {
                try {
                    Connection con = this.dataSource.getConnection();
                    ...
                }
                catch (SQLException ex) {
                  ...
                }
              }

              public void storePet(Pet pet) {
                try {
                    Connection con = this.dataSource.getConnection();
                    ...
                }
                catch (SQLException ex) {
                  ...
                }
              }

              ...
          }



          如你所見,代碼相當(dāng)直接。我們使用一個(gè)簡(jiǎn)單的Java對(duì)象,而事務(wù)管理由事務(wù)代理來處理,這個(gè)我們會(huì)在下面討論。
          注意在PetClinic示例應(yīng)用中實(shí)際的基于JDBC的Clinic實(shí)現(xiàn)利用了Spring的JDBC支持類來避免直接使用JDBC的API。雖然Spring的事務(wù)管理也可以與普通的基于JDBC實(shí)現(xiàn)一起工作,就向上面的示例。

          定義事務(wù)代理
          除了JdbcClinic實(shí)例以外,配置中也定義了一個(gè)事務(wù)代理。如果愿意這個(gè)代理所暴露的實(shí)際接口也可以顯式定義。默認(rèn)情況下,所有由目標(biāo)對(duì)象實(shí)現(xiàn)的接口都暴露出來,在這個(gè)例子中就是應(yīng)用的Clinic服務(wù)接口。

          從客戶端的觀點(diǎn)來看,"clinic" bean只是這個(gè)應(yīng)用的Clinic接口的實(shí)現(xiàn)。客戶端不需要知道這會(huì)被一個(gè)事務(wù)代理所處理。這就是接口的能力:一個(gè)直接的目標(biāo)對(duì)象的引用可以容易的被一個(gè)實(shí)現(xiàn)相同接口的代理所代替—在這兒就是一個(gè)隱式創(chuàng)建事務(wù)的代理。
          代理的具體事務(wù)行為會(huì)由為根據(jù)特定的方法或方法命名模式而定義的事務(wù)屬性來驅(qū)動(dòng),就像下面的例子所示:
          清單3:
          <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
          <prop key="store*">PROPAGATION_REQUIRED</prop>


          Key屬性決定代理將為方法提供什么樣的事務(wù)行為。這個(gè)屬性的最重要部分就是事務(wù)傳播行為。下面是一些可選的屬性值:
          1、PROPAGATION_REQUIRED --支持當(dāng)前的事務(wù),如果不存在就創(chuàng)建一個(gè)新的。這是最常用的選擇。
          2、PROPAGATION_SUPPORTS --支持當(dāng)前的事務(wù),如果不存在就不使用事務(wù)。
          3、PROPAGATION_MANDATORY --支持當(dāng)前的事務(wù),如果不存在就拋出異常。
          4、PROPAGATION_REQUIRES_NEW --創(chuàng)建一個(gè)新的事務(wù),并暫停當(dāng)前的事務(wù)(如果存在)。
          5、PROPAGATION_NOT_SUPPORTED --不使用事務(wù),并暫停當(dāng)前的事務(wù)(如果存在)。
          6、PROPAGATION_NEVER --不使用事務(wù),如果當(dāng)前存在事務(wù)就拋出異常。
          7、PROPAGATION_NESTED --如果當(dāng)前存在事務(wù)就作為嵌入事務(wù)執(zhí)行,否則與PROPAGATION_REQUIRED類似。

          前6 個(gè)事務(wù)策略與EJB的CMT類似,而且使用相同的常量名,因此對(duì)EJB開發(fā)人員來說是很親切的。第7個(gè)策略PROPAGATION_NESTED是 Spring提供的一個(gè)變體:他需要事務(wù)管理器(如DataSourceTransactionManager)提供類似JDBC3.0那樣的保存點(diǎn) API來嵌套事務(wù)行為或者通過
          JTA支持嵌套事務(wù)。

          事務(wù)屬性中的readOnly標(biāo)識(shí)指示相應(yīng)的事務(wù)應(yīng)該作為一個(gè)只讀事務(wù)來優(yōu)化。這是一個(gè)優(yōu)化提示:一些事務(wù)策略在這種情況下可以得到很好的性能優(yōu)化,如使用ORM工具如Hibernate或TopLink時(shí)避免臟數(shù)據(jù)檢查(“flush”嘗試)。

          在事務(wù)屬性中還有一個(gè)“timeout”選項(xiàng)來定義事務(wù)的超時(shí)秒數(shù)。在JTA中,這個(gè)屬性會(huì)簡(jiǎn)單地傳遞給J2EE服務(wù)器的事務(wù)協(xié)調(diào)器并被正確地解釋。

          使用事務(wù)代理
          在 運(yùn)行時(shí),客戶端會(huì)取得一個(gè)“clinic”引用并轉(zhuǎn)換為Clinic接口,然后調(diào)用如loadPet或storePet方法。這就隱式地使用了 Spring的事務(wù)代理,通過“事務(wù)解釋器”在目標(biāo)對(duì)象中注冊(cè);這樣一個(gè)新的事務(wù)就創(chuàng)建了,然后具體的工作就會(huì)代理給JdbcClinic的目標(biāo)方法。
          圖1示例了一個(gè)使用“建議鏈”并到達(dá)最后目標(biāo)的AOP代理的潛在概念。在這個(gè)示例中,唯一的建議是一個(gè)事務(wù)解釋器用來包裝目標(biāo)方法的事務(wù)行為。這是一種用來在聲明性事務(wù)功能下使用的基于代理的AOP。



          Figure 1. An AOP proxy with an advisor chain and a target at the end

          例如,一個(gè)PetClinic應(yīng)用的WEB層組件可以執(zhí)行ServletContext定位來獲取Spring WebApplicationContext的引用并且獲取受管理的“clinic”BEAN:
          清單4:
          WebApplicationContext ctx = 
             WebApplicationContexUtils.getWebApplicationContext(servletContext);
          Clinic clinic = (Clinic) ctx.getBean("clinic);

          Pet pet = new Pet();
          pet.setName("my new cat");

          clinic.storePet(pet);


          在 調(diào)用storePet()之前,Spring的事務(wù)代理隱式地創(chuàng)建一個(gè)事務(wù)。當(dāng)storePet()調(diào)用返回時(shí),事務(wù)將提交或回滾。缺省情況下任何 RuntimeException或Error將導(dǎo)致回滾。實(shí)際的提交或回滾可以是可以定義的:Spring的事務(wù)屬性支持“回滾規(guī)則”的概念。

          例如,我們可以可以引入一個(gè)強(qiáng)制的PetClinicException并且告訴事務(wù)代理在拋出異常時(shí)回滾:
          清單5:
          <prop key="load*">PROPAGATION_REQUIRED,readOnly,-PetClinicException</prop>
          <prop key="store*">PROPAGATION_REQUIRED,-PetClinicException</prop>


          這兒也有一個(gè)類似的“提交規(guī)則”語(yǔ)法,指示特定的異常將觸發(fā)一次提交。
          注 意上面示例的顯式定位引用的方法只是一種訪問受Spring管理BEAN的方法的變化,可以用在任何WEB資源如servlet或filter。在構(gòu)建基 于Spring自身的MVC框架時(shí),BEAN可以直接被注射到WEB控制器中。當(dāng)然也支持在如Struts, WebWork, JSF, and Tapestry框架中訪問Spring管理BEAN。詳情可以參考Spring的文檔。

          PlatformTransactionManager策略

          Spring 事務(wù)支持的核心接口是org.springframework.transaction.PlatformTransactionManager。所有 Spring的事務(wù)分離功能都會(huì)委托給PlatformTransactionManager(傳給相應(yīng)的TransactionDefinition實(shí) 例)來做實(shí)際的事務(wù)執(zhí)行。雖然PlatformTransactionManager接口可以直接調(diào)用,但通常應(yīng)用只需要配置一個(gè)具體的事務(wù)管理器并且通 過聲明性事務(wù)來分離事務(wù)。

          Spring提供幾種不同的PlatformTransactionManager實(shí)現(xiàn),分為如下兩個(gè)類別:
          1、 本地事務(wù)策略—支持單一資源的事務(wù)(通常是單個(gè)數(shù)據(jù)庫(kù)),其包括 org.springframework.jdbc.datasource.DataSourceTransactionManager和 org.springframework.orm.hibernate.HibernateTransactionManager。
          2、全局事務(wù)管理—支持可能跨越多個(gè)資源的全局事務(wù)。其相應(yīng)的類為org.springframework.transaction.jta.JtaTransactionManager,將事務(wù)委托給遵循JTA規(guī)范的事務(wù)協(xié)調(diào)器(通常為J2EE服務(wù)器,但不是強(qiáng)制的)。

          PlatformTransactionManager 抽象的主要價(jià)值在于應(yīng)用不再被綁定在特定的事務(wù)管理環(huán)境。相反,事務(wù)策略可以很容易地切換—通過選擇不同的 PlatformTransactionManager實(shí)現(xiàn)類。這就使得應(yīng)用代碼與聲明事務(wù)分離保持一致,而不需要考慮應(yīng)用組件所使用的環(huán)境了。

          例 如,應(yīng)用的初始版本可能布署在Tomcat上,與單個(gè)Oracle數(shù)據(jù)庫(kù)交互。這可以方便地利用Spring的事務(wù)分離特性,只要選擇基于JDBC的 DataSourceTransactionManager作為使用的事務(wù)策略。Spring會(huì)分離事務(wù),而JDBC驅(qū)動(dòng)會(huì)執(zhí)行相應(yīng)的原始JDBC事務(wù)。

          相 同應(yīng)用的另一個(gè)版本可能會(huì)布署在WebLogic服務(wù)器上,使用兩個(gè)Oracle數(shù)據(jù)庫(kù)。應(yīng)用代碼和事務(wù)分離不需要改變。唯一不同的是選擇作為 JtaTransactionManager事務(wù)策略,讓Spring來分離事務(wù)而WebLogic服務(wù)器的事務(wù)協(xié)調(diào)器來執(zhí)行事務(wù)。

          JTA UserTransaction與JTA TransactionManager比較
          讓我們來看一下Spring對(duì)JTA支持的細(xì)節(jié)。雖然并非經(jīng)常需要考慮這個(gè)細(xì)節(jié)但了解相關(guān)的細(xì)節(jié)還有必要的。對(duì)簡(jiǎn)單的用例如前面章節(jié)的示例,標(biāo)準(zhǔn)的JtaTransactionManager定義已經(jīng)足夠了,
          缺 省的Spring JtaTransactionManager設(shè)置會(huì)從標(biāo)準(zhǔn)JNDI位置(J2EE規(guī)范所定義的java:comp/UserTransaction)獲取 JTA的javax.transaction.UserTransaction對(duì)象。這對(duì)大部分標(biāo)準(zhǔn)J2EE環(huán)境來說已經(jīng)足夠了。

          然而, 缺省的JtaTransactionManager不能執(zhí)行事務(wù)暫停(也就是說不支持PROPAGATION_REQUIRES_NEW和 PROPAGATION_NOT_SUPPORTED)。原因就在于標(biāo)準(zhǔn)的JTA UserTransaction接口不支持事務(wù)的暫停和恢復(fù),而只支持開始和完成新的事務(wù)。

          為了實(shí)現(xiàn)事務(wù)的暫停,需要一個(gè) javax.transaction.TransactionManager實(shí)例,他提供了JTA定義的標(biāo)準(zhǔn)的暫停和恢復(fù)方法。不幸的是,J2EE沒有為 JTA TransactionManager定義標(biāo)準(zhǔn)的JNDI位置!因此,我們需要使用廠商自己的定位機(jī)制。
          清單6:
          <bean id="transactionManager" 
             class="org.springframework.transaction.jta.JtaTransactionManager">
               <property name="transactionManagerName">
                  <value>vendorSpecificJndiLocation</value>
               </property>
          </bean>



          J2EE 本質(zhì)上沒有考慮將JTA TransactionManager接口作為公共API的一部分。JTA規(guī)范自身定義了將TransactionManager接口作為容器集成的想 法。雖然這是可以理解的,但是JTA TransactionManager的標(biāo)準(zhǔn)JNDI位置還是可以增加一定的價(jià)值,特別是對(duì)輕量級(jí)容器如Spring,這樣任何J2EE服務(wù)器就可以用統(tǒng) 一的方式來定位JTA TransactionManager了。

          不僅Spring的JtaTransactionManager可以從 訪問中獲益,O/R映射工具如Hibernate, Apache OJB, and Kodo JDO也能得到好處,因?yàn)樗麄冃枰贘TA環(huán)境中執(zhí)行緩存同步的能力(釋放緩存意味著JTA事務(wù)的完成)。這種注冊(cè)事務(wù)同步的能力只有JTA TransactionManager接口才能提供,而UserTransaction是處理不了的。因此,這些工具都需要實(shí)現(xiàn)自己的 TransactionManager定位器。

          為JTA TransactionManager定義標(biāo)準(zhǔn)的JNDI位置是許多底層軟件供應(yīng)商最期望J2EE實(shí)現(xiàn)的功能。如果J2EE5.0的規(guī)范制定團(tuán)隊(duì)能夠認(rèn)識(shí) 到這個(gè)特性的重要性就太好了。幸運(yùn)地是,高級(jí)J2EE服務(wù)器如WebLogic Server已經(jīng)考慮將JTA TransactionManager作為公共的API包含在擴(kuò)展功能中。

          在WebLogic JTA中實(shí)現(xiàn)Spring的事務(wù)分離
          在WebLogic Server中,JTA TransactionManager官方的JNDI位置定義為javax.transaction.TransactionManager。這個(gè)值可以 在Spring的JtaTransactionManager中作為“transactionManagerName”使用。原則上這樣就可以在 WebLogic's JTA系統(tǒng)中實(shí)現(xiàn)事務(wù)暫停了,也就是說支持PROPAGATION_REQUIRES_NEW和PROPAGATION_NOT_SUPPORTED行 為。

          除了標(biāo)準(zhǔn)的JtaTransactionManager和其支持的通用配置選項(xiàng)外,Spring還提供了一個(gè)專用的WebLogicJtaTransactionManager適配器來直接利用WebLogic的JTA擴(kuò)展。

          在享受自動(dòng)探測(cè)WebLogic的JTA TransactionManager的便利之外,他提供超越標(biāo)準(zhǔn)JTA的三個(gè)重要特性:
          1、事務(wù)命名—暴露出Spring的事務(wù)名給WebLogic Server,使得Spring事務(wù)在WebLogic的事務(wù)監(jiān)聽器可見。缺省的,Spring會(huì)使用聲明性事務(wù)的完整方法名。
          2、每事務(wù)隔離級(jí)別—將Spring事務(wù)屬性中定義的隔離級(jí)別應(yīng)用到WebLogic JTA事務(wù)中。這使得每個(gè)事務(wù)都可以定義數(shù)據(jù)庫(kù)的隔離級(jí)別,而這是標(biāo)準(zhǔn)JTA所不支持的。
          3、強(qiáng)制事務(wù)恢復(fù)—即使在暫停的事務(wù)被標(biāo)識(shí)為回滾時(shí)也可以恢復(fù)。這需要使用WebLogic的擴(kuò)展TransactionManager接口來調(diào)用forceResume()方法。

          image
          Figure 2. WebLogic Server's transaction monitor (click the image for a full-size screen shot)

          Spring的WebLogicJtaTransactionManager有效地為基于Spring的應(yīng)用提供了WebLogic Server事務(wù)管理的全部功能。這使得Spring事務(wù)分離成為一種能與EJB CMT竟?fàn)幍漠a(chǎn)品,而且提供了相同級(jí)別的事務(wù)支持。

          Spring and EJB CMT

          如 上所示,Spring的POJO聲明性事務(wù)分離可以作為一種除傳統(tǒng)EJB CMT這外的選擇。但是Spring與EJB并不是完成互斥的,Spring的應(yīng)用上下文也可以作為EJB fa&ccedil;ade的后臺(tái)來管理數(shù)據(jù)訪問(DAO)和其他細(xì)紋理的業(yè)務(wù)對(duì)象。

          在EJB情景中,事務(wù)是由EJB CMT來驅(qū)動(dòng)的。對(duì)Spring來說,數(shù)據(jù)訪問支持特性會(huì)自動(dòng)檢測(cè)到這樣的環(huán)境并且采用相應(yīng)的事務(wù)。例如,Spring對(duì)Hibernate的支持能夠提 供隱式的資源管理,即使是EJB驅(qū)動(dòng)的事務(wù),甚至可以在不需要修改任何DAO代碼的情況下提供相同的語(yǔ)義。
          Spring有效的解耦了DAO實(shí)現(xiàn)與實(shí)際的運(yùn)行環(huán)境。DAO可以參與Spring的事務(wù)就像參與EJB CMT事務(wù)一樣。這不僅簡(jiǎn)化在其他環(huán)境中的重用,而且更方便在J2EE容器外進(jìn)行測(cè)試。

          結(jié)論
          Spring框架為J2EE和非J2EE環(huán)境提供了全量的事務(wù)分離的特性,特別表現(xiàn)在POJO的聲明性事務(wù)上。他用一種靈活而非侵入式的方式為非EJB環(huán)境中的事務(wù)分離提供了便利。與EJB不同,這樣的事務(wù)性POJO應(yīng)用對(duì)象可以很容易的被測(cè)試和在J2EE容器外補(bǔ)重用。

          Spring 提供了各種事務(wù)策略,如JtaTransactionManager是用來代理J2EE服務(wù)器的事務(wù)協(xié)調(diào)器,而JDBC DataSourceTransactionManager是用來為簡(jiǎn)單的JDBC DataSource(就是單一目標(biāo)數(shù)據(jù)庫(kù))執(zhí)行事務(wù)。Spring可以很容易為不同的環(huán)境通過后臺(tái)配置的簡(jiǎn)單修改來調(diào)整事務(wù)策略。

          超越 標(biāo)準(zhǔn)的JTA支持,Spring為WebLogic Server的JTA擴(kuò)展提供了完善的集成,可以支持高級(jí)特性如事務(wù)監(jiān)視和每事務(wù)隔離級(jí)別。通過對(duì)WebLogic Server的特殊支持,基于Spring的應(yīng)用可以完全利用WebLogic Server的事務(wù)管理功能。

          Spring事務(wù)分離是繼 EJB CMT之外的另一種可選方式,特別是對(duì)那些基于POJO的輕量級(jí)架構(gòu)。在那只是因?yàn)檫x擇LSSB(本地?zé)o狀態(tài)會(huì)話BEAN)來應(yīng)用聲明性事務(wù)的情況下,基 于Spring的POJO服務(wù)模型是一種可行的選擇,他提供了非常高層的靈活性、可測(cè)試性和重用性。

          資源
          &#8226;JTA - The JTA specification JTA規(guī)范
          &#8226;WebLogic JTA - Documentation of WebLogic's JTA extensions WebLogic  JTA擴(kuò)展文檔

          關(guān)于作者
          Juergen Hoeller是Spring框架的創(chuàng)始人之一
          posted @ 2005-12-16 22:39 Dion 閱讀(5079) | 評(píng)論 (1)編輯 收藏

          物流軟件廠商出路何在?


          ■本報(bào)記者 凡曉芝



          “幾乎沒有一個(gè)物流企業(yè)是贏利的”、“幾乎沒有一家物流軟件提供商是賺錢的”、“幾乎沒有一個(gè)物流信息化項(xiàng)目是成功的”,這樣的判斷背后的事實(shí)是,物流軟件廠商的日子比黃蓮還苦。

          “大家都知道做ERP苦,我們做物流軟件的比ERP更苦。”坐在我對(duì)面的某物流軟件公司總經(jīng)理臧先生一臉無(wú)奈。三年前,他從一家國(guó)外的ERP公司跳槽到此,因?yàn)槟菚r(shí)候他對(duì)“物流信息化高潮已經(jīng)來臨”深信不疑。

          “現(xiàn)在,物流軟件對(duì)于我們集團(tuán)來說就是一塊雞肋,食之無(wú)味,棄之可惜”。事實(shí)上,從2004年起,業(yè)界就曾風(fēng)傳該公司將被賣掉。熬到2005年年底,情況并沒有好轉(zhuǎn)而業(yè)績(jī)更糟。“年后我得另謀生路了,”臧先生說,“但物流軟件公司,打死我也不去。”

          比做ERP還苦

          關(guān)于物流信息化有幾段“名言”:“幾乎沒有一個(gè)物流企業(yè)是贏利的”、“幾乎沒有一家物流軟件提供商是賺錢的”、“幾乎沒有一個(gè)物流信息化項(xiàng)目是成功的。”這樣的判斷就如同“中國(guó)ERP實(shí)施成功率為零”一樣的偏頗。但是其背后的事實(shí)是,“物流軟件廠商的日子比黃蓮還苦”。

          隨便拿起一個(gè)物流軟件公司的宣傳冊(cè),煙草、醫(yī)藥、石油、化工、快速消費(fèi)品等行業(yè)巨頭的成功案例都赫然在目。“這些 動(dòng)輒上百萬(wàn)元,甚至上千萬(wàn)元的項(xiàng)目其實(shí)沒有幾個(gè)是賺錢的。”另一家物流軟件公司的總經(jīng)理陳先生對(duì)記者說:“就好比夏奈爾每年的時(shí)裝發(fā)布會(huì),只是為了一個(gè)品 牌形象,實(shí)在不指望它能賺錢。大型物流信息化項(xiàng)目基本上就是賠本賺吆喝。”

          也有人甘愿“賠本賺吆喝”,上海博科公司營(yíng)銷副總經(jīng)理周龍就認(rèn)為大型項(xiàng)目就算不賺錢也是“值”的。“我們所做的 諸如中國(guó)石油這樣的大型的物流信息化項(xiàng)目,哪怕利潤(rùn)不是很高,我們也要堅(jiān)持做,不但堅(jiān)持做還要讓客戶滿意,這對(duì)我們品牌宣傳很重要。”周龍認(rèn)為,博科在國(guó) 內(nèi)物流信息化領(lǐng)域的地位日漸強(qiáng)勢(shì),與這些大項(xiàng)目的實(shí)施緊密相關(guān)。

          但是更多的廠商是“賠了夫人又折兵”,被無(wú)休止的“爛尾工程”拖入泥沼。臧先生所在的物流公司在2004年希望 能上演“絕地反擊”,為了項(xiàng)目和銷售額幾乎到了不擇手段的地步,在競(jìng)標(biāo)中時(shí)常被稱為“攪屎棍”,以超低價(jià)格接連接了四個(gè)大的項(xiàng)目,但是災(zāi)難也就接踵而至, 項(xiàng)目實(shí)施無(wú)休無(wú)止,甚至有客戶難以忍受該公司的“折磨”而要和廠商對(duì)簿公堂。

          之所以要這樣冒險(xiǎn)一搏的原因還是惡劣的競(jìng)爭(zhēng)環(huán)境使然。“泥沙俱下,魚龍混雜”是一位業(yè)內(nèi)人士對(duì)目前物流軟件市場(chǎng) 的看法。由于2002年前后,業(yè)界爆炒物流信息化高潮來臨,引來無(wú)數(shù)的投機(jī)者和投資者,物流軟件提供商的數(shù)量急劇增多,據(jù)中國(guó)物流信息化中心初步統(tǒng)計(jì),我 國(guó)物流軟件廠商有500多家,此外還有一些知名的國(guó)外物流商、IT公司和咨詢公司也在中國(guó)的物流軟件市場(chǎng)中淘金。

          北京海淀東北旺一家物流公司的老板金先生對(duì)記者說,今年至少有30家軟件公司找過他,報(bào)價(jià)最低5萬(wàn)元,最高的是 300萬(wàn)元。“說實(shí)話,我被他們?cè)礁阍胶苛耍恢谰烤乖撓嘈耪l(shuí)。也不知道他們說的是不是真話。”事實(shí)上,據(jù)業(yè)內(nèi)人士透露,聲稱做物流信息軟件的公司中 70%到80%的企業(yè)都屬于“混水摸魚”,他們就是在傳統(tǒng)倉(cāng)儲(chǔ)管理軟件或者運(yùn)輸管理軟件的基礎(chǔ)上加以修改,然后就宣稱這是一套物流信息系統(tǒng)。“懂物流的企 業(yè)不多,卻有更多的企業(yè)在攪局。”

          三個(gè)錯(cuò)位

          業(yè)內(nèi)分析人士認(rèn)為,造成目前物流軟件廠商集體陷入困境的原因是“三個(gè)錯(cuò)位”。

          AMT企業(yè)資源研究中心資深顧問趙楊說:“對(duì)物流信息化的爆炒而帶來的惡性競(jìng)爭(zhēng)是物流軟件廠商陷入困境的主要原因。”而從另外一個(gè)角度看,其實(shí)就是市場(chǎng)需求滯長(zhǎng)而廠商預(yù)期過高之間的錯(cuò)位使然。

          據(jù)中國(guó)物流信息中心預(yù)測(cè),2004~2006年,每年物流軟件市場(chǎng)的總體規(guī)模將維持在20億~35億元人民幣之 間,年增長(zhǎng)率大約為5%左右。但是在此之前,大多數(shù)的物流軟件廠商都對(duì)這個(gè)市場(chǎng)過于樂觀,使得市場(chǎng)競(jìng)爭(zhēng)者蜂擁而至。像用友金蝶這樣的專業(yè)ERP公司都成立 物流部門做企業(yè)物流業(yè)務(wù),國(guó)外巨頭如i2、SSA、曼哈頓等憑借在自動(dòng)化倉(cāng)庫(kù)、物流供應(yīng)鏈執(zhí)行等方面強(qiáng)勢(shì)進(jìn)入市場(chǎng),加之?dāng)?shù)不勝數(shù)的“散兵游勇”型軟件公 司,物流信息化市場(chǎng)掠食者眾多。

          博科公司營(yíng)銷中心副總經(jīng)理周龍說:“第三方物流企業(yè)的信息化被認(rèn)為是拉動(dòng)物流行業(yè)信息化的關(guān)鍵,但事實(shí)上,第三 方物流企業(yè)目前多數(shù)不賺錢,在信息化方面投入很小,而且這一塊市場(chǎng)遠(yuǎn)遠(yuǎn)沒有啟動(dòng)起來。”寄望已久的市場(chǎng)需求爆發(fā)點(diǎn)遲遲未到,但是參與競(jìng)爭(zhēng)者卻“人滿為 患”,物流軟件廠商打單難,賺錢更難也就不難理解。

          其次是大系統(tǒng)和中小企業(yè)需求之間的錯(cuò)位。根據(jù)2005年中國(guó)物流信息中心對(duì)中國(guó)企業(yè)物流信息化現(xiàn)狀和趨勢(shì)的調(diào)查 情況看,未建立信息系統(tǒng)的企業(yè)絕大部分是小型企業(yè),預(yù)計(jì)投資大于100萬(wàn)的比例只有1.8%,37.5%的企業(yè)投資低于10萬(wàn)元,這些數(shù)據(jù)可以看出,小型 企業(yè)的信息化需求潛力巨大。

          但是正如中國(guó)物流與采購(gòu)聯(lián)合會(huì)副會(huì)長(zhǎng)戴定一所指出,由于多數(shù)開發(fā)商認(rèn)為,“物流信息化市場(chǎng)在低端不具備開發(fā)價(jià) 值”,多數(shù)廠商只是把眼光盯著幾個(gè)有限的行業(yè)和著名企業(yè),對(duì)小企業(yè)、小項(xiàng)目并不上心。比如海運(yùn)、船運(yùn)和集裝箱運(yùn)輸這些高端物流項(xiàng)目確實(shí)利潤(rùn)豐厚,項(xiàng)目動(dòng)輒 就是幾百萬(wàn),甚至10個(gè)億投入,但是這一塊市場(chǎng)基本上都被IBM、HP、SAP等等廠商所把持。

          國(guó)內(nèi)很多專業(yè)的物流軟件廠商寧愿為極少的“高端客戶”和大項(xiàng)目打得頭破血流,也不愿意針對(duì)中小企業(yè)客戶開發(fā)“適銷對(duì)路”的產(chǎn)品。因此大家都做得很辛苦。

          第三是高成本與低回報(bào)之間的錯(cuò)位。軟件開發(fā)成本高昂,但是物流企業(yè)出得起的錢卻很少,軟件廠商基本上處于“做一個(gè) 虧一個(gè)”的惡性循環(huán)中。趙楊對(duì)記者說:“物流行業(yè)的需求千差萬(wàn)別,比如快速消費(fèi)品和醫(yī)藥、煙草行業(yè)就是完全不同的流程,要把物流軟件做成標(biāo)準(zhǔn)化產(chǎn)品基本上 是不可能的。目前市場(chǎng)上聲稱的所謂標(biāo)準(zhǔn)化的產(chǎn)品只不過是一種噱頭而已。”由于絕大多數(shù)的物流項(xiàng)目都是定制化開發(fā),不但項(xiàng)目實(shí)施的周期長(zhǎng),而且項(xiàng)目常常陷入 無(wú)休止的修改和二次開發(fā)的泥沼之中。

          做“全”或者做“專”

          “再辛苦,我們也要堅(jiān)持到底。”一家專業(yè)物流軟件廠商的總經(jīng)理對(duì)記者說 :“不經(jīng)歷風(fēng)雨怎么見彩虹。”這其實(shí)是多數(shù)物流軟件廠商的心態(tài),在投入了上千萬(wàn)元的資金、積累了一些客戶基礎(chǔ)以后,完全放棄物流信息化市場(chǎng)確實(shí)是很難。更何況,物流信息化可以說“前途是光明的”。

          “但是物流軟件廠商必須要改變,如果抱殘守缺肯定只有死路一條。”采訪中趙楊對(duì)物流軟件廠商的未來并不十分看好。 “如果說要求解,物流軟件廠商要么做全,要么做專。”趙楊認(rèn)為,所謂做“全”就是不要局限于物流軟件,而是由物流供應(yīng)鏈切入到包括ERP、SCM、CRM 等業(yè)務(wù)系 ;而做“專”就是盯準(zhǔn)一個(gè)行業(yè)做產(chǎn)品和客戶。

          物流信息化市場(chǎng)涉及的目標(biāo)客戶非常廣泛,如儲(chǔ)運(yùn)公司、國(guó)際貨代公司、船代公司、包裹快遞公司、郵政部門、零售公司、大型工業(yè)企業(yè)中的物流部門等等。不同市場(chǎng)對(duì)信息化的需求既有一定的共性,又存在一定差異。物流軟件廠商必須要立足自身實(shí)際情況,確定合適的目標(biāo)市場(chǎng)。

          也有分析人士認(rèn)為,做專的另一種方式是提供細(xì)分化產(chǎn)品。物流管理是一個(gè)綜合的系統(tǒng)工程,整個(gè)物流運(yùn)作是運(yùn)輸、儲(chǔ) 存、裝卸、搬運(yùn)、包裝、流通加工、配送等基本功能的有機(jī)結(jié)合。如果廠商能圍繞部門級(jí)信息系統(tǒng)如倉(cāng)儲(chǔ)管理系統(tǒng)、運(yùn)輸管理系統(tǒng)、配送管理系統(tǒng)、報(bào)關(guān)管理系統(tǒng)、 貨代管理系統(tǒng)、快遞管理系統(tǒng)等開發(fā)出一些精深且有特色的產(chǎn)品,將能夠更大程度地增強(qiáng)自身的市場(chǎng)競(jìng)爭(zhēng)力。

          事實(shí)上,諸如上海博科這樣的選擇堅(jiān)守的物流軟件廠商也開始進(jìn)行業(yè)務(wù)的調(diào)整。記者在采訪中了解到,針對(duì)大型企業(yè)物 流的需求,博科將改變以前的單個(gè)物流項(xiàng)目的做法。周龍說:“我們的物流系統(tǒng)將和博科集團(tuán)財(cái)務(wù)、ERP、供應(yīng)鏈等產(chǎn)品組合成為一個(gè)整體,做成一個(gè)整體解決方 案提供給客戶。”而對(duì)于中小型的物流公司,則通過平臺(tái)化產(chǎn)品,通過渠道銷售的方式滿足客戶需求。而記者在采訪中得知,另一家知名的物流軟件廠商招商迪辰也 “正在業(yè)務(wù)調(diào)整中”。

          “物流軟件廠商無(wú)論做專還是做全,都必須要做精”,趙楊強(qiáng)調(diào)說:“廠商的產(chǎn)品和品牌形象至關(guān)重要。”根據(jù)中國(guó)物 流信息中心最新的調(diào)查報(bào)告,物流客戶對(duì)開發(fā)商最看重的幾項(xiàng)條件分別是:成功案例(85%)、咨詢與服務(wù)經(jīng)驗(yàn)(73.3%)、品牌46.7%、價(jià)格因素 45%。對(duì)未建系統(tǒng)的企業(yè)調(diào)查顯示,對(duì)有成功案例最看重(40%),其次是品牌(26%)。由此可見,在未來的競(jìng)爭(zhēng)中,物流軟件廠商必須得拿“牌子”見 客,拿產(chǎn)品說話。

          (計(jì)算機(jī)世界報(bào) 2005年12月05日 第47期 E5)

          posted @ 2005-12-15 16:15 Dion 閱讀(826) | 評(píng)論 (1)編輯 收藏

          Google編程大賽入圍賽250分真題


          Problem Statement
          ????
          You are given a String[] cityMap representing the layout of a city. The city
          consists of blocks. The first element of cityMap represents the first row of
          blocks, etc. A 'B' character indicates a location where there is a bus stop.
          There will be exactly one 'X' character, indicating your location. All other
          characters will be '.'. You are also given an int walkingDistance, which is the
          maximum distance you are willing to walk to a bus stop. The distance should be
          calculated as the number of blocks vertically plus the number of blocks
          horizontally. Return the number of bus stops that are within walking distance of
          your current location. Definition
          ????
          Class:
          BusStops
          Method:
          countStops
          Parameters:
          String[], int
          Returns:
          int
          Method signature:
          int countStops(String[] cityMap, int walkingDistance)
          (be sure your method is public)
          ????

          Constraints
          -
          cityMap will contain between 1 and 50 elements, inclusive.
          -
          Each element of cityMap will contain between 1 and 50 characters, inclusive.
          -
          Each element of cityMap will contain the same number of characters.
          -
          Each character of each element of cityMap will be 'B', 'X', or '.'.
          -
          There will be exactly one 'X' character in cityMap.
          -
          walkingDistance will be between 1 and 100, inclusive.
          Examples
          0)

          ????
          {"...B.",
           ".....",
           "..X.B",
           ".....",
           "B...."}
          3
          Returns: 2
          You can reach the bus stop at the top (3 units away), or on the right (2 units
          away). The one in the lower left is 4 units away, which is too far. 1)

          ????
          {"B.B..",
           ".....",
           "B....",
           ".....",
           "....X"}
          8
          Returns: 3
          A distance of 8 can get us anywhere on the map, so we can reach all 3 bus stops.
          2)

          ????
          {"BBBBB",
           "BB.BB",
           "B.X.B",
           "BB.BB",
           "BBBBB"}
          1
          Returns: 0
          Plenty of bus stops, but unfortunately we cannot reach any of them.
          3)

          ????
          {"B..B..",
           ".B...B",
           "..B...",
           "..B.X.",
           "B.B.B.",
           ".B.B.B"}
          3
          Returns: 7

          This problem statement is the exclusive and proprietary property of TopCoder,
          Inc. Any unauthorized use or reproduction of this information without the prior
          written consent of TopCoder, Inc. is strictly prohibited. (c)2003, TopCoder,
          Inc. All rights reserved.

          posted @ 2005-12-15 13:14 Dion 閱讀(1200) | 評(píng)論 (0)編輯 收藏

          面向方面的Annotation

          作者:Bill Burke,Enterprise JavaBeans 第四版合著者

          譯者:yahveyeye





          版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
          作者:Bill Burke;yahveyeye
          原文地址:http://www.onjava.com/pub/a/onjava/2004/08/25/aoa.html
          中文地址:http://www.matrix.org.cn/resource/article/44/44052_Annotation+Aop.html
          關(guān)鍵詞: Annotation Aop

          Annotation 是J2SE5.0的一項(xiàng)新功能,它允許您附加元數(shù)據(jù)到Java構(gòu)建中。同時(shí),面向方面編程(AOP)是一個(gè)相當(dāng)新的技術(shù),它可以使您封裝某些行為,這些行 為是在使用面向?qū)ο螅∣O)技術(shù)時(shí)會(huì)更為混亂,困難甚至是不可能完成。這兩項(xiàng)技術(shù)結(jié)合起來給框架開發(fā)者開發(fā)的APIs更好的表達(dá)方式。本文深入結(jié)合這些技 術(shù),使用Jboss AOP框架,以不同的代碼范例向您展示如何結(jié)合兩者來實(shí)際地?cái)U(kuò)展Java 語(yǔ)言。

          相關(guān)文章:
          Java Annotation入門:
          http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html

          Annotation概述

          首 先讓我們給出這兩項(xiàng)技術(shù)的一個(gè)概述。Annotation是JDK5.0的新功能,它在JSR-175規(guī)范中有詳細(xì)定義。它們?cè)试S您以安全的方法定義元數(shù) 據(jù)并應(yīng)用到類,方法,構(gòu)造程序,字段或參數(shù)中。對(duì)于你們中熟悉XDoclet的人來說,Annotation將非常直觀,您可以用來聲明標(biāo)簽以產(chǎn)生代碼。 兩者的主要不同是Annotation是Java語(yǔ)言的一部分而XDoclet標(biāo)簽可能會(huì)打錯(cuò)并且難以創(chuàng)建。我喜歡用例子來說明,所以讓我們展示一個(gè)簡(jiǎn)單 的例子。

          要定義一個(gè)Annotation,您所要做的就是聲明一個(gè)特殊類型的Java接口。

          清單1:Orange.java
          package org.jboss.collors;
          public @interface Orange{}


          定義了這個(gè)接口,您就可以用來提供更多的描述給您的Java元素。
          清單2:Foo.java
          package org.jboss.examples;
          public class Foo
          {
            @Orange void someMethod();
            @Orange private int someField;
          }


          那么我們可以用Annotation來干什么呢?一些人想用Annotation來產(chǎn)生代碼并替代XDoclet,其他人,象J2EE和EJB3.0專家組,將它視為部署描述符的替代。本文談?wù)撛贏OP中如何使用Annotation

          AOP概述

          有許多的文章和書籍解釋AOP到底是什么,例如Graham O'Regan的ONJava文章“Introduction to Aspect-Oriented Programming."我將在本文給出一個(gè)快速的概覽,但我鼓勵(lì)您在線做更多的研究。

          假設(shè)您要添加代碼到一個(gè)應(yīng)用程序去測(cè)試調(diào)用一個(gè)特定的java方法所需的總的時(shí)間。該代碼可能看起來如下:
          清單3:
          public class BankAccount
          {
          public void withdraw(double amount)
          {
          long startTime = System.currentTimeMillis();
          try
          {
          // Actual method body...
          }
          finally
          {
          long endTime = System.currentTimeMillis() - startTime;
          System.out.println("withdraw took: " + endTime);
          }
          }
          }


          雖然這些代碼能夠正常工作,但這個(gè)方法有一些問題:
          1.它難以打開和關(guān)閉測(cè)試,您必須在try/finally塊中對(duì)每個(gè)方法或購(gòu)置函數(shù)手工增加代碼以進(jìn)行基準(zhǔn)測(cè)試。
          2。這一輪廓代碼并不真正屬于貫穿整個(gè)應(yīng)用的代碼。它使得您的代碼臃腫并難以理解,因?yàn)槟仨殞⒂?jì)時(shí)放在try/finally塊中。
          3、如果您想擴(kuò)展它的功能以包含一個(gè)方法或是失敗計(jì)數(shù),或甚至是注冊(cè)這些統(tǒng)計(jì)數(shù)據(jù)到一個(gè)更為復(fù)雜的報(bào)告機(jī)制中,您必須修改大量不同文件(又一次)。

          Metrics 類提供了一個(gè)什么是橫切(cross-cutting)關(guān)系的完美,簡(jiǎn)潔的小例子。Jboss AOP以一種含蓄的方式提供了一個(gè)簡(jiǎn)單的方法來封裝和應(yīng)用這樣的關(guān)系,這樣某些象度量操作代碼不會(huì)弄亂您的編碼。讓我們稍為深入到Jboss AOP一些來看看如何實(shí)現(xiàn)。
          為了使用Jboss AOP封裝度量功能,您首先需要定義一個(gè)方面來指出該度量行為。
          清單4:
          public class Metrics
          {
          public Object profile(MethodInvocation invocation) throws Throwable
          {
          long startTime = System.currentTimeMillis();
          try
          {
          return invocation.invokeNext();
          }
          finally
          {
          long endTime = System.currentTimeMillis() - startTime;
          java.lang.reflect.Method m = invocation.getMethod();
          System.out.println("method " + m.toString() +
          " time: " + endTime + "ms");
          } }
          }


          一 個(gè)方面只是一個(gè)具有定義了您想要附加到您的對(duì)象模型的行為的普通Java類。這些方法的簽名必須返回一個(gè)java.lang.Object并且必須具有一 個(gè)(并且只有一個(gè))Jboss AOP 調(diào)用對(duì)象參數(shù),它被用來封裝方法,構(gòu)造函數(shù)或字段調(diào)用。方法名可以是任何你想要的并且當(dāng)您綁定該方面到您的代碼片斷時(shí)被引用。

          下面要做的事情就是實(shí)際應(yīng)用方面到您想要它勾勒一個(gè)方法的執(zhí)行的某個(gè)程序點(diǎn)。大多數(shù)AOP框架提供了一個(gè)指向表達(dá)式語(yǔ)言,在此處您可以定義您想要某個(gè)方面行為被附加到的位置。下面是在Jboss AOP中的做法。
          清單5:jboss-aop.xml
          <aop>
          <aspect class="Metrics"/>

          <bind pointcut="execution(public void BankAccount->withdraw(double amount))">
          <advice name="profile" aspect="Metrics"/>
          </bind>
          </aop>


          采用在Metrics.java中對(duì)方面的定義和jboss-aop.xml中的指向定義,該度量代碼現(xiàn)在以含蓄而又透明地應(yīng)用到BankAccount.withdraw()方法中并能在勾勒代碼不再需要時(shí)輕易地移除。
          對(duì)于Jboss AOP更多的信息,請(qǐng)查詢分發(fā)包中的指南。其中具有大約20個(gè)例子來帶領(lǐng)您漫游如何使用Jboss AOP框架。

          噓!現(xiàn)在我們已經(jīng)進(jìn)行了一個(gè)概覽,讓我們深入到本文的中心內(nèi)容。我將再次給您提供一些例子,因?yàn)檫@是我所知道的講授一個(gè)新的概念的最好的方法。
          正如我前面說的,Annotation加上AOP幾乎是給予您擴(kuò)展Java語(yǔ)言的能力。Annotation提供了聲明新的,可兼容的,類型安全的語(yǔ)法機(jī)制。AOP提供了封裝和應(yīng)用新的行為到一個(gè)語(yǔ)法表達(dá)式的機(jī)制。

          方法Annotation和AOP

          讓 我們看看如何使用方法Annotation和AOP。使用Annotation和AOP并應(yīng)用到一個(gè)方法類似于使用Java的synchronized關(guān) 鍵字。當(dāng)您設(shè)定一個(gè)方法為synchronized,您在告訴JVM:您想該方法在被調(diào)用時(shí)以一種特殊的方式進(jìn)行。Annotation允許您定義一個(gè)新 的關(guān)鍵字來觸發(fā)您自己的特殊的定制行為。AOP給予您封裝這一行為的能力并將其“編織”進(jìn)該方法的執(zhí)行中。再次的,這一概念的最佳描述是通過一個(gè)例子。

          讓我們假設(shè)我們想要添加新的語(yǔ)法,使用該語(yǔ)法使得我們可以在方法被標(biāo)簽為@Oneway時(shí),在后臺(tái)以另一個(gè)線程調(diào)用這個(gè)void方法。可以象這樣使用新的語(yǔ)法:

          清單6:
          Import org.jboss.aspects.Oneway;
          public class Foo
          {
            @Oneway public static void someMethord(){…}
          public static void main(String[] args){
          somMethod();//executes in
          backgroud
          }
          }


          當(dāng)someMethod()在main中被調(diào)用,它將異步運(yùn)行,這樣main中的代碼可以并行執(zhí)行其他任務(wù)。
          要實(shí)現(xiàn)這一功能,首先要在一個(gè)Annotation中為我們的@Oneway標(biāo)簽定義新的Java語(yǔ)法.
          清單7:Oneway.java
          package org.jboss.aspects;

          import java.lang.annotation.ElementType;
          import java.lang.annotation.Target;

          @Target({ElementType.METHOD})
          public @interface Oneway {}


          夠簡(jiǎn)單的。@Target標(biāo)簽允許您縮小Annotation可以應(yīng)用的地方。在本例中,我們的@OnewayAnnotation只能應(yīng)用到一個(gè)方法。記住,這些都是J2SE5.0百分之百可用的純Java。
          下面要做的事是定義一個(gè)封裝我們的@Oneway行為的方面類。
          清單8:OnewayAspect.java

          package org.jboss.aspects;

          public OnewayAspect
          {
          private static class Task implements Runnable
          {
          private MethodInvocation invocation;

          public Task(MethodInvocation invocation)
          {
          this.invocation = invocation;
          }
          public void run()
          {
          try { invocation.invokeNext(); }
          catch (Throwable ignore) { }
          }
          }


          public Object oneway(MethodInvocation invocation) throws Throwable
          {
          MethodInvocation copy = invocation.copy();
          Thread t = new Thread(new Task(copy));
          t.setDaemon(false);
          t.start();
          return null;
          }
          }


          這 個(gè)方面夠簡(jiǎn)單。oneway()方法拷貝invocation,創(chuàng)建一個(gè)線程,在后臺(tái)啟動(dòng)整個(gè)調(diào)用并返回。我們可以想象一個(gè)更為復(fù)雜的例子:使用J2SE 5.0 java.util.concurrent包中的某些新的Executors,但這些代碼很有希望闡明了如何基于這個(gè)例子構(gòu)建更為復(fù)雜的實(shí)現(xiàn)。
          最后必須要做的事情是指定當(dāng)@OnewayAnnotation在一個(gè)方法中聲明時(shí)觸發(fā)OnewayAspect應(yīng)用的指向表達(dá)式。

          清單9:jboss-aop.xml
          <aop>
          <aspect class="org.jboss.aspects.OnewayAspect"/>
          <bind pointcut="execution(void *->@org.jboss.Oneway(..))">
          <advice name="oneway"
          aspect="org.jboss.aspects.OnewayAspect"/>
          </bind>
          </aop>


          該 指向表達(dá)式規(guī)定任何具有@Oneway標(biāo)簽的void方法都應(yīng)該有OnewayAspect.oneway()方法在它本身執(zhí)行前被執(zhí)行。隨著 Annotation,方面和現(xiàn)在定義的指向表達(dá)式,@Oneway語(yǔ)法現(xiàn)在可以用于您的應(yīng)用程序中。一個(gè)簡(jiǎn)單,清晰,易于實(shí)現(xiàn)的方法來擴(kuò)展Java 語(yǔ)言!

          字段Annotation和AOP

          讓 我們看看如何使用字段Annotation和AOP。使用Annotation和AOP,您可以改變一個(gè)對(duì)象的字段或是作為一個(gè)類的靜態(tài)成員的實(shí)際存儲(chǔ)方 式。在這個(gè)例子里我們要完成的是當(dāng)您將一個(gè)字段(靜態(tài)或是成員)標(biāo)記上@ThreadBased,盡管是將它存儲(chǔ)在 java.lang.ThreadLocal,但它的值依然正常。當(dāng)然,您可以直接使用ThreadLocal變量,但問題是ThreadLocal并非 一個(gè)類型并且您必須使用“麻煩的”(好,它們并沒有那么羅嗦)get()和set()方法。那么我們現(xiàn)在做的就是創(chuàng)建一個(gè)ThreadLocal類型的字 段。我們主要的將創(chuàng)建一個(gè)稱為@Thradbased變量的新的Java字段類型。
          象這樣使用新的類型:
          清單10:
          import org.jboss.aspects.Threadbased;
          public class Foo
          {
          @Threadbased private int counter;
          }


          為了實(shí)現(xiàn)這個(gè)功能,我們必須先定義Annotation
          清單11:Threadbased.java
          package org.jboss.aspects;
          import java.lang.annotation.ElementType;
          import java.lang.annotation.Target;
          @Target({ElementType.FIELD})
          public @interface Threadbased {}



          夠簡(jiǎn)單。@Target標(biāo)簽允許您縮小Annotation可以應(yīng)用的地方。在本例中,我們的@ThreadbasedAnnotation只能應(yīng)用到字段。
          下面的事情是定義封裝我們的ThreadLocal行為的方面。
          清單12:ThreadbasedAspect.java
          package org.jboss.aspects;
          import org.jboss.aop.joinpoint.*;
          import java.lang.reflect.Field;
          public class ThreadbasedAspect
          {
          private ThreadLocal threadbased = new ThreadLocal();
          public Object access(FieldReadInvocation invocation)
          throws Throwable
          {
          // just in case we have a primitive,
          // we can't return null
          if (threadbased.get() == null)
          return invocation.invokeNext();
          return threadbased.get();
          }
          public Object access(FieldWriteInvocation invocation)
          throws Throwable
          {
          threadbased.set(invocation.getValue());
          return null;
          }
          }


          ThreadbasedAspect 封裝到一個(gè)Java字段的訪問。它里面具有一個(gè)專門的ThreadLocal變量跟蹤thradlocal變?yōu)橐粋€(gè)特殊的字段。它還有一個(gè)單獨(dú)的 access()方法,該方法根據(jù)一個(gè)字段的get或set方法是否被調(diào)用決定它是否被調(diào)用。這些方法委托給ThreadLocal來獲得字段的當(dāng)前值。
          最后,我們必須定義一個(gè)指向表達(dá)式,當(dāng)@ThreadbasedAnnotation在某個(gè)字段被指定時(shí)觸發(fā)ThreadbasedAspect的應(yīng)用。
          清單13:jboss-aop.xml
          <aop>
          <aspect class="org.jboss.aspects.ThreadbasedAspect" scope="PER_JOINPOINT"/>
          <bind pointcut="field(* *->@org.jboss.aspects.Threadbased)">
          <advice name="access"
          aspect="org.jboss.aspects.ThreadbasedAspect"/>
          </bind>
          </aop>


          只 有當(dāng)我們具有多個(gè)@Threadbased變量定義在同一個(gè)類時(shí),我們需要為每個(gè)靜態(tài)字段分配一個(gè)ThreadbasedAspect實(shí)例。對(duì)于成員變 量,我們需要為每個(gè)字段,每個(gè)對(duì)象實(shí)例分配一個(gè)ThreadbasedAspect實(shí)例。為了促進(jìn)這一行為,方面定義通過設(shè)定實(shí)例為 PER_JOINPOINT限制方面類的實(shí)例何時(shí)和何地被分配出去的范圍。如果我們不做限制,Jboss
          AOP會(huì)只分配一個(gè)ThreadbasedAspect實(shí)例并且不同的字段會(huì)共享相同的ThreadLocal接口——這不是我們所希望的。

          好就這樣。一個(gè)清晰容易的擴(kuò)展Java來指定一個(gè)新的特殊類型的方法。注意:該特殊的方法來自Jboss AOP束。

          依賴注入

          字 段Annotation和AOP可以使用的一個(gè)有趣的地方是依賴注入。依賴注入是關(guān)于對(duì)象聲明它們需要什么信息,配置或服務(wù)引用以及運(yùn)行時(shí)自動(dòng)注入這些依 賴而不是用代碼明確地在一個(gè)注冊(cè)中心查找。在J2EE領(lǐng)域,獲得javax.transaction.TransactionManager服務(wù)的訪問并 未標(biāo)準(zhǔn)化并且實(shí)際上不同的廠商有不同的實(shí)現(xiàn)。許多框架開發(fā)者需要使用TransactionManager來實(shí)現(xiàn)定制事務(wù)服務(wù)。使用字段 AnnotationAOP提供依賴注入并抽取出一個(gè)需要TransactionManager的組件如何引用它的細(xì)節(jié)是一個(gè)了不起的方法。讓我們定義一 個(gè)方面,它將注入一個(gè)TransactionManager引用到一個(gè)字段值中。

          首先,我們?cè)俅味x我們的Annotation。

          清單14:Inject.java
          package org.jboss.aspects;
          import java.lang.annotation.ElementType;
          import java.lang.annotation.Target;
          @Target({ElementType.FIELD})
          public @interface Inject {}


          下面我們將定義方面類,它封裝了TransactionManager的解析。該方面是特定于JBoss應(yīng)用服務(wù)器,但您可以定義為每個(gè)廠商定義不同的實(shí)現(xiàn)。
          清單15:InjectTMAspect.java
          package org.jboss.aspects;
          import org.jboss.aop.joinpoint.*;
          import java.lang.reflect.Field;
          import javax.transaction.TransactionManager;
          import org.jboss.tm.TxManager;
          public InjectTMAspect
          {
          private TransactionManager tm = TxManager.getInstance();
          public Object access(FieldReadInvocation invocation)
          throws Throwable {
          return tm;
          }
          public Object access(FieldWriteInvocation invocation)
          throws Throwable {
          throw new RuntimeException(
          "Setting an @Injected variable is illegal");
          }
          }


          最后,我們必須定義XML綁定來觸發(fā)當(dāng)@Inject標(biāo)簽應(yīng)用到一個(gè)字段時(shí)InjectTMAspect的應(yīng)用。指向表達(dá)式基本上說明了對(duì)任意一個(gè)標(biāo)記為@Inject的TransactionManager字段應(yīng)用InjectTMAspect。
          清單16:
          <aop>
          <aspect class="org.jboss.aspects.InjectTMAspect"/>
          <bind pointcut="field(javax.transaction.TransactionManager *->@org.jboss.aspects.Inject)">
          <advice name="access"
          aspect="org.jboss.aspects.InjectTMAspect"/>
          </bind>
          </aop>


          現(xiàn)在Annotation、方面類和XML綁定已經(jīng)定義,我們可以在我們的代碼中使用了。
          清單17:
          import javax.transaction.TransactionManager;
          import org.jboss.aspects.Inject;
          public class MyTransactionalCache
          {
          @Inject private TransactionManager tm;
          ...
          }


          更多預(yù)打包例子

          Jboss AOP不僅僅是關(guān)于AOP框架。它還有一個(gè)豐富的方面庫(kù),您可以直接在您的應(yīng)用中使用。在這個(gè)庫(kù)中是一個(gè)比我們現(xiàn)在在本文展示的例子更為復(fù)雜的 Annotation方面集。這些方面包括異步調(diào)用,事務(wù)劃分,事務(wù)鎖定和基于角色的安全。讓我們簡(jiǎn)要地瀏覽一下以提供給您一個(gè)更好的關(guān)于 Annotation和AOP共同工作的考慮。

          異步方面
          Jboss AOP異步方面允許您定義任何方法為異步的,這樣它可以在后臺(tái)被執(zhí)行。這對(duì)于我們的@Oneway例子來說有些困難,因?yàn)樗褂肙swego并行包中的執(zhí) 行器工具,并為那些具有一個(gè)返回類型的方法提供了一個(gè)方法來異步地接收回響應(yīng)。要使用這個(gè)方面,您只需標(biāo)記一個(gè)方法為@Asybchronous.
          清單18:
          public Foo {
          @Asynchronous public int someMethod(int someArg) {...}
          }


          @Asynchronous 標(biāo)簽的應(yīng)用做了一些事情。與在本文中的@Oneway例子一樣,它應(yīng)用一個(gè)在后臺(tái)運(yùn)行該方法的方面。而且,采用@Asynchronous標(biāo)簽,您并不僅 限于void方法并可于實(shí)際上返回一個(gè)值的方法進(jìn)行交互。當(dāng)@Asynchronous標(biāo)簽被應(yīng)用,它強(qiáng)制Foo類實(shí)現(xiàn) AsynchronousFacade接口。在AOP領(lǐng)域,這稱為接口引入(interface introduction)。AsynchronousFacade接口允許您預(yù)測(cè)一個(gè)響應(yīng)或以超時(shí)限定等待一個(gè)響應(yīng)。最好用一個(gè)例子來解釋。
          清單19:
          Foo foo = new Foo();
          someMethod(555); // executes in background
          AsynchronousFacade facade = (AsynchronousFacade)foo;
          AsynchronousResponse response = facde.waitForResponse();
          System.out.println(response.getReturnValue());


          您可以啟動(dòng)多個(gè)不同對(duì)象的多個(gè)不同方法的多個(gè)調(diào)用,并異步積累它們的響應(yīng)。

          事務(wù)鎖定
          有時(shí)在J2EE事務(wù)期間而不是一個(gè)方法執(zhí)行,構(gòu)造函數(shù)調(diào)用或同步塊執(zhí)行期間同步一個(gè)對(duì)象或類會(huì)很有用。對(duì)這類事務(wù)同步或鎖定,Jboss AOP發(fā)明了@TxSynchronized關(guān)鍵字。您可以使用@TxSynchronized在任意成員或靜態(tài)方法已經(jīng)構(gòu)造函數(shù)上。
          清單20:
          import org.jboss.aspects.txlock.TxSynchronized;
          public FooBar
          {
          @TxSynchronized public FooBar() {}
          @TxSynchronized static void staticMethod() {}
          @TxSynchronized public memberMethod() {}
          }


          如 果一個(gè)被標(biāo)記為@TxSynchronized的構(gòu)造函數(shù)或靜態(tài)方法被調(diào)用,類的鎖監(jiān)視器會(huì)在事務(wù)執(zhí)行期間被保持著。如果一個(gè)標(biāo)記為 @TxSynchronized的成員方法被調(diào)用,該對(duì)象實(shí)例的鎖監(jiān)視器將被保持直到目前的事務(wù)提交或回退。控制該行為的方面也將做死鎖檢測(cè)并在發(fā)生死鎖 時(shí)拋出RuntimeException。

          J2EE 散餐(原文法文:a la carte)之:事務(wù)劃分
          EJB3.0已經(jīng)定義了一些Annotation進(jìn)行事務(wù)劃分。Jboss AOP在此基礎(chǔ)上構(gòu)建。這樣您可以通過指定Annotation應(yīng)用事務(wù)劃分到任意方法(靜態(tài)或成員)以及任何Java類構(gòu)造函數(shù)。
          清單21:
          import org.jboss.aspects.tx.*;
          public class Foo
          {
          @Tx(TxType.REQUIRED) public Foo {}
          @Tx(TxType.REQUIRESNEW) public static createFoo() {
          return new Foo();
          }
          }


          J2EE 散餐之:基于角色的安全
          EJB 3.0也定義了一些Annotation實(shí)現(xiàn)基于角色的安全。Jbos AOP是基于此構(gòu)建的,所以您可以應(yīng)用基于角色的安全到任何的字段或方法(靜態(tài)或成員)已經(jīng)構(gòu)造函數(shù)。
          清單22:
          import org.jboss.aspects.security.*;
          @SecurityDomain("LDAP Repository")
          public class Foo
          {
          @Permissions({"admin"}) public Foo() {}
          @Permissions({"developer"}) public static int status;
          @Permissions({"anybody"}) public static void doSomething() {...}
          }


          EJB演變

          隨 著AOP與EJB規(guī)范一起漸漸成熟,我真正希望發(fā)生的是EJB規(guī)范定義的Annotation將能在任何上下文作為新的Java語(yǔ)言的形容詞被使用,而不 是讓它們有限的使用在會(huì)話bean中。想象一下,一個(gè)真正的無(wú)狀態(tài)bean僅僅成為一個(gè)明文Java類的一個(gè)靜態(tài)方法集。
          清單23:
          public MySessionBean
          {
          @Tx(TxType.REQUIRED) public static doSomething() {...}
          }


          無(wú)論如何,這些關(guān)于AOP和EJB的討論很可能就是為了EJB4.0。

          結(jié)論
          并非是限制J2SE5.0Annotation用于代碼生成,Annotation和AOP可以被結(jié)合起來提供新的能力給框架開發(fā)者。這一結(jié)合允許開發(fā)者定義新的具有行為附加到其上的Java語(yǔ)法。基本上,以安全的方式擴(kuò)展Java語(yǔ)言的能力已盡在掌握。

          資源
          ·Matrix-Java開發(fā)者社區(qū):http://www.matrix.org.cn
          ·onjava.com:onjava.com

          關(guān)于作者
          Bill Burke :JBoss 首席架構(gòu)師.
          posted @ 2005-12-15 13:12 Dion 閱讀(1632) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共5頁(yè): 上一頁(yè) 1 2 3 4 5 下一頁(yè) 
          主站蜘蛛池模板: 芦山县| 邵阳县| 磴口县| 涞水县| 阳谷县| 太原市| 沙田区| 连南| 桐柏县| 靖州| 石台县| 东安县| 连州市| 六枝特区| 喀喇| 休宁县| 沂源县| 莱州市| 辽源市| 河曲县| 苍南县| 辽阳市| 涿鹿县| 米脂县| 保山市| 金门县| 武清区| 京山县| 宜黄县| 江达县| 儋州市| 庄河市| 会宁县| 崇阳县| 通道| 高唐县| 柳州市| 弥勒县| 乌鲁木齐市| 焉耆| 商河县|