你沒看錯,就是這個題目:即使是Java系統也會變成“遺留”系統。每當我們想起遺留系統時,我們就會想起那些存儲著大量文件數據並只能用COBOL訪問的嘎吱嘎吱作響的大型主機。但事實是,Java已經是一門具有15年歷史的開發語言,用Java寫就的成千上萬的系統已經成功運行了十年甚至更久。
因此,考慮到許多讀者都會工作在某個遺留的Java系統上,我根據自己的經驗特地攢了這八個技巧,來幫助團隊更新和激活他們的遺留Java應用。
技巧1:使用分析器
分析器提供了任何其他工具無法提供的功能,從而能夠深入檢查你的應用。如果你的應用已經有一年多時間沒有被分析過了,那麼它肯定會有大塊大塊的低效代碼,潛伏在某個黑暗的角落。市面上有許多不同的或免費或商業的分析器。對於CPU分析,我最喜歡的是JProfiler,因為它足夠強大能分析出大多數問題,同時易於設置,尤其當你使用它內建的設置向導的時候。而診斷內存問題時,我最親睐的工具是Eclipse Memory Analyzer,因為它使用的是記錄在磁盤上的索引,而不是把整個堆的快照放到內存中。
通常來說,隱藏著的易耗盡CPU的代碼包括低效的hashCode()或者equals()方法(在卷動JTable時以及使用Java collection類時,它們會被調用上百萬次),以及一些出人意料的出自Sun之手的低效類,比如SimpleDateFormat。
分析器可能會明顯地讓你的應用變得很慢,所以你一定要在測試環境中使用它。
技巧2:監控數據庫使用狀況
分析器除了可以顯示你的應用過度占用CPU時鐘的細節,它們也可以對你的應用在哪些地方長時間做了數據庫的操作給出提示。但更好的用來監控數據使用的工具,是像Proactive DBA或者HP Diagnostics,或者任何其他來自於你的數據庫產品廠商的工具。這些工具可以告訴你,哪些代碼做了長時間的SQL調用,以及哪些代碼在短時間內對同一行做了多次調用。來自數據庫廠商的工具還可以幫助發現那些阻塞了其他調用的查詢;雖然在我的經驗裡,這樣的阻塞問題基本不過是些簡單的、低效的SQL用法。
我寫了一個新的工具叫做jdbcGrabber,它可以讓你以可視化的形式描述出哪些代碼正在訪問哪些數據表。通過這種可視化呈現,你可以很容易發現那些多次訪問數據庫中不同部分信息的代碼,從而將其調整為一次合並的請求。
技巧3:構建和部署自動化
許多遺留系統缺乏一種完全自動化的方式,來構建它們的代碼,更不用說自動部署了。自動化構建和部署對於提高遺留系統開發者的效率來說,是一種簡單直接而又低風險的方式,而且通常不需要修改代碼。
沒有自動化的構建和部署過程,新的開發者不得不重新發明輪子,跟那些前輩們早就斗爭過的同樣問題重新來斗,而且每次重復的部署問題發生,開發者都會發明出不同的解決方案。
雖然Maven是一款卓越的而且使用廣泛的構建工具,但它對你的源碼樹結構以及庫依賴有著固執的要求,所以把它用在遺留應用中會有點困難。但足夠優秀的Ant應該更易於使用,因為它處理起遺留代碼結構更加靈活,也更容易部分采用,而不是全盤采用。
技巧4:自動化你的操作並使用JMX
另外一種提高遺留應用的效率但不會帶來修改代碼的風險的方式是,改善它的運維。許多內部開發的企業系統,一般都需要大量出人意料的手把手指導和維護,即使這樣是不應該的。
既有的Java功能可以通過使用JMX很容易地暴露給負責運營的人們,而不會帶來負面影響。許多開發者對JMX比較熟悉是因為,他們用JMX來跟JBoss和WebLogic這樣的應用服務器進行交互,但他們不清楚把JMX用在他們自己的應用中是多麼方便。任何Java class都可以通過JMX暴露出來,幾乎沒什麼負面效果,也沒有什麼風險。
比如,如果你的應用有一個本地的靜態HashMap作為cache,你就可以通過JMX來暴露功能,從而很容易地清除那個cache。
一旦應用通過JMX暴露,運維團隊或者開發者就可以以良好的方式來操作應用,無需直接訪問運行著應用的機器。
技巧5:創建單元測試
一旦你對遺留系統的修改破壞了某個功能,你所面臨的最大障礙之一就來到了。一些工具宣稱能對代碼進行反向工程,並為其創建單元測試,但我對這些工具沒有太多的信心。要想有足夠的信心,你的單元測試的確覆蓋了你期望它們覆蓋的代碼,你就不得不親自創建它們。
很幸運,為遺留代碼創建單元測試並沒有一開始感覺上的那樣困難。我使用了Michale Feathers在Working Effectively with Legacy Code一書中講解的“遺留代碼修改算法”:
確認修改點
找出測試點
打破依賴
編寫測試
修改並重構
有效使用這個算法的竅門在於第3點:打破依賴。有很多技術可以用來干這個,但其中大多數都是關於移除靜態引用以及在接口和facade下隱藏外部引用和復雜代碼。一旦你具有這樣打破依賴的感覺了,接觸遺留代碼就不會是一件讓你提心吊膽的事情了。
技巧6:殺死無用代碼
雖然無用代碼可能看起來無害,但它們實際上往往會是無聲的殺手。原因在於只要無用代碼還在代碼庫中,負責維護的程序員就不會非常確信,代碼是真的無用還是只是看起來無用。感受過前幾次修改所帶來的痛苦的維護者都知道,即使是靜態代碼分析也不能證明代碼是真的無用了。比如,十年前一些聰明的程序員可能會通過數據庫中的字符串值來驅動Java reflection調用業務邏輯(別笑,我不止一次看到過這樣)。
因此,殺死無用代碼應用是第一優先級的任務。雖然Emma通常被認為是一種單元測試覆蓋工具,但它可以用來偵測無用代碼。當你把Emma注入到JVM中,它就可以追蹤到哪些代碼執行了,哪些沒有。在你的開發環境中,把Emma和一個完整的測試周期相結合使用,你就會知道哪些代碼活著還是死了。
技巧7:采用一種“順從”方式構建代碼
遺留應用不可能一次清理完畢。在現實中,開發團隊必須利用任何一次機會,來改善遺留代碼。但許多團隊對目前代碼的情況都倍感失望,而無法考慮他們究竟該怎麼做。“代碼實在太糟糕了,”開發者說。
冷漠是最大的錯誤。遺留應用之所以還存活著是因為,它們依然有用,而且和所有有用的應用一樣,他們的用戶會繼續想要修改它們。如果團隊抓住機會定義一個可以達到的願景:希望應用會是什麼樣子,然後做出逐步增量的改變,他們就會離距離最終的願景更進一步。
沒有這樣的願景,團隊的每個成員就會做出任何他/她所認為最正確的事情。一個人會使用Spring JdbcTemplate而另一個人會開始使用iBATIS/MyBatis。雖然每個人都真正期望改善這個應用,但事實上他們會讓事情變得更糟,因為他們是在不同的方向上使力,使已經復雜的結構更加混亂。
技巧8:升級你的JRE
當我告訴一些團隊Sun(現在是Oracle)早在2009年11月就已經宣稱不在繼續對JDK 1.5的支持時,他們仍然覺得驚訝不已。這不僅僅是立刻要升級JRE到1.6的事情。那些歷經磨難的團隊,還記得從1.1升級到1.2或者1.4升級到 1.5時所發生的一切,他們可能對這樣的升級還感到猶豫。但我的經驗是,這樣的升級會很平滑,而且會給應用帶來一次顯著的免費的性能飛躍。另外,JDK 1.6還帶來許多有用的、免費的運維和分析工具,來幫助診斷那些你這些年一直備受困擾的垃圾回收問題。
八個技巧之外
上面精心挑選出來的每個技巧,基本都是易於采用,並風險相對要低。但還有很多其他的方式來改善遺留應用,讓應用改善後看起來就像是新的一樣。
首先,現在的開放源代碼庫生態系統給過去大部分的遺留Java系統帶來了生機。許多遺留系統會有土生土長、完全自定義的各種子系統:工作流引擎、規則引擎、模板引擎、用戶接口框架以及對象關系映射層等等。這些土生土長的組件中的任意一個,都可以被一個免費的開放源代碼庫替換掉,而且更加智能並足夠強壯。這樣一對一的替換可以很大程度上消除一次全部替換所帶來的維護上的困難。
其次,是時候好好看看你自己的遺留應用的設計問題了。雖然改變設計遠比僅僅升級JRE要復雜得多,但它也會給你的投資帶來更大的回報。對於大量邏輯都存儲在數據庫存儲過程中的應用,可以考慮把那些邏輯提高到應用層,從而可以受益於集群服務器,並更容易進行單元測試。如果一個設計把表示層跟業務邏輯層綁定得太緊,那你就可以把它們分開,這樣增加新潮的iPhone界面也會很容易實現。在各個子系統之間的同步調用也可以轉換成異步、基於消息的調用,這在彈性和性能上都會是重要的改善。
最後,為了讓你從Java遺留應用中多活兩到四年,我建議你雇傭一個對這樣系統有經驗的專家。就像一個外科醫生做精妙的大腦手術一樣,有經驗的專家通常可以為遺留系統中的問題找到更好的解決方案,從而帶來更多的好處以及低風險。
對於那些期望吸取更深內容的讀者,我建議這本我讀過的最好的關於遺留系統的書:Michale Feather的Working Effectively with Legacy Code。任何工作在遺留系統上的開發者都會從這本書中受益。