|
下面的類計算并緩存了一個總和,并且在另一個類中打印這個總和
class Cache{
static {
initializeIfNecessary();
}
private static int sum;
public static int getSum(){
initializeIfNecessary();
return sum;
}
private static boolean initialized = false;
private static synchronized void initializeIfNecessary(){
if(!initialized){
for(int i=0; i < 100; i++ )
sum+=i;
initialized = true;
}
}
}
pubic class Client {
pubilc static void main(String[] args){
System.out.println(Cache.getSum());
}
}
程序是一個典型的半開循環,應該打印出1到99的整數總和,即4950,但程序運行打印的為9900,是預期值的兩倍。
程序在確保sum在使用之前就已經初始化這個問題上,遇到了麻煩。程序結合了惰性初始化和積極初始化,還使用上了同步,以確保緩存在多線程環境下也能工作,但程序無法完成預期的工作。
程序輸出打印是控制標志initialized 的問題。qq上一個朋友問的一本書里的問題,我也找到那本書了,是惰性初始化(static初始化)的問題,控制標志在static完成初始化后又重新被構造器賦值為false,初始化總和值的方法被執行了兩次。
大概一年以前參與到一個大型信息管理軟件系統的維護工作中,這個系統是B/S結構,主要負責某省煤炭資源運銷管理,應用在了鐵路這個渠道管理上,我們的主要的系統維護工作重心在鐵路管理維護上,另外還負責一些網站信息發布管理功能的維護。這個管理系統有自己的一套網絡體系,因為我并沒有參與其中的網絡體系的搭建與后期的維護工作,只是大概的了解一些:
1.網絡體系的結構為樹狀拓撲結構,主要依據該公司的組織機構建立,該公司組織機構分為總公司一級,分公司一級,縣級公司一級以及直接負責煤炭運銷具體的工作的公司一級。網絡體系以此而建,也分為四級網絡。
2.使用一套c/s體系的軟件,完成基礎數據的收集,并定時上報到縣級公司或者直屬的分公司,然后將數據匯總分析后通過這套軟件提交,統一儲存到總公司數據庫服務器。
3.根據該行業的業務,每年全省的煤炭運量,特別是通過鐵路渠道的運量,在年初通過簽訂的全年合同決定,該年的業務也以此運量為準。
這套軟件成型與2002年,在當時的條件限制下這是一套最為有效的軟件工作體系了,但也有其缺陷:
1. 盡管基礎數據收集大都已經為通過電子稱量系統實現自動數據提取,但在上報過程中,仍有可能對數據進行篡改,軟件為此專門增設了一個子功能系統,但使用情況不容樂觀,收效甚微。
2. 數據統一存儲在總公司數據庫服務器中,方便管理,在最初的設計中,數據庫沒有設計負載分流以及鏡像備份的等處理,隨著系統的使用,數據量的增加,數據庫負載增大,軟件反應速度越來越慢。
這個缺陷在今年年初運量提報和每個月初的各個分公司的運量提報中尤為明顯,數據庫出現多次當機情況,處理過多次,但收效不明顯,在使用高峰期需要注意數據庫的使用情況。
這個系統的網絡部分就寫這么多吧,畢竟不太熟悉,還是摻雜了軟件的非網絡部分,:-)。下一篇開始寫這個軟件的web架構部分,主要是mvc框架和dao框架部分。
The Architect (dedicated non-programming technical decision maker and problem solver for business): 架構師(專用非編程技術決策者,業務問題解決者)
|
完成該系統后,所完成的后期維護工作中,最大的以部分就是數據統計查詢分析功能的完善了。 系統軟件框架提供了一套報表生成系統來完成數據統計查詢的功能。這條報表生成系統可以生成excel和pdf兩種格式的統計報表。 這個系統使用報表的流程大致為: 1.每個功能都提供一系列的報表,在軟件中這些報表以二維字符串數組的數據格式保存,每個字符串數組對象包括該報表的唯一鍵值,報表名稱,報表條件提供頁面以及生成該報表的具體的Java類。 2.選擇需要生成的報表,系統將該報表的唯一鍵值傳入統一的報表控制類,首先是跳轉到生成該報表的條件頁面,按照功能劃分報表的主要原因就是每個功能的報表條件一致,可以使用統一的控制類。 3.提交條件后,統一的報表控制類會調用該報表的生成類,這些類需要繼承統一的接口,提供了一個統一的方法,返回數據類型為Vector集合的對象,這個Vector對象容器數據對象要求為: 兩個Vector對象,一個為表頭Vector,一個為表體Vector,這兩個Vector對象中存儲的數據為字符串數組對象。 在具體應用中,只要將數據填充到字符串數組中,再依次放入Vector中,報表框架會生成報表。 這個框架的一個確定就是只能接受字符串數組對象,如果生成excel格式的報表,無法根據生成的報表進行計算,必須手動的將數據調整為數字型。
|
大家喝的是啤酒,這時你入座了,給自己倒了杯可樂,這叫低配置;給自己倒了杯啤酒,這叫標準配置;給自己倒了杯茶水,這茶的顏色還跟啤酒一樣,這叫木馬;給自己倒了杯可樂,還滴了幾滴醋,不僅顏色跟啤酒一樣,不冒熱氣還有泡泡,這叫超級木馬;你同事給你到了杯白酒,這叫推薦配置。
人到齊了,酒席開始了。你現一個人喝了一小口,這叫單元測試;你跟旁邊的人說哥們咱們隨意,這叫交叉測試;但是他說不行,這杯要干了,這叫壓力測試;于是你說那就大家一起來吧,這叫內部測試;這時候boss向全場舉杯,這叫公開測試。 菜過三巡,你就不跟他們客氣了,你向對面的人敬酒,這叫p2p;他回敬你,你又再敬他,這叫TCP;你向一桌人挨個敬酒,這叫令牌環;你說只要是兄弟就干了這杯,這叫廣播。 有一個人過來向這卓敬酒,你說不行,你先過了我這關,這叫防火墻。你的小弟們過來敬你酒,這叫一對多。 酒過三巡后,你也該活動活動了,你一桌一桌的走,這叫輪詢;你突然看見某一桌的漂亮mm,走了過去,這叫優先級;你去了坐下來就不打算走了,這叫死循環;你的老大舉杯邀你過去,你只好走過去,這叫激活事件。 你向一桌敬酒,他們說:“不行,不行,我們都喝白的。”于是你也喝白的。這叫本地化;你向boss敬酒,可是boss被圍起來,你只能站在外圍,這叫排隊;你終于到了內圍,小心翼翼的向前一步,這叫訪問臨界區;你拍照boss的肩膀說哥們咱們再喝已被,這叫越界。 |
一個程序員對自己的未來很迷茫,于是去問上帝:“萬能的上帝啊,請你告訴我,我的未來會怎樣?” 上帝說:“我的孩子,你去問Lippman,他現在領導的程序員的隊伍可能是地球上最大的。” 于是他去問Lippman,Lippman說:“程序員的未來就是駕馭程序員。” 這個程序員對這個未來不滿意,于是他又去問上帝:“萬能的上帝呀,請你告訴我,我的未來會怎樣?” 上帝說:我的孩子,你去問Gates,他現在所擁有的財產可能是地球上最多的。” 于是他去問Gates,Gates說:“程序員的未來就是榨取程序員。” 這個程序員對這個未來不滿意,于是他又去問上帝:“萬能的上帝啊,請你告訴我,我的未來會怎樣?” 上帝說:“我的孩子,你去問侯捷,他寫的計算機書的讀者可能是地球上最多的。” 于是他去問侯捷。 侯捷說:“程序員的未來就是誘惑程序員。” 這個程序員對這個未來不滿意,于是又去問上帝:“萬能的上帝啊,請你告訴我,我的未來會怎樣?” 上帝搖搖頭:“唉,我的孩子,你還是別當程序員了。” |
該系統軟件框架對sql操作進行了封裝,并提供了一批API幫助客戶程序員更為簡便的進行數據庫操作。這些api包括:
這個數據庫框架還提供了一些方便編寫代碼的操作,例如拼裝sql語句,提供了一個這樣的方法:MakeUp類。代碼如下: java 代碼
這個makeUp方法會將sql語句拼裝好,@表示數字占位,#表示字符占位。 這個系統的數據庫操作管理大致就是這樣了,比較簡單,總的來說就是對dbconncetion 操作進行了重新封裝,方便客戶程序員的使用。
|
維護的這個軟件系統使用了一個比較復雜的系統權限管理系統,原本的設計是將用戶對軟件的使用權限控制到以最末級的菜單,并根據公司組織機構的設計,上級組織單位可以將自身所獲得的權限繼續授權到自己的下級公司。
為了授權權限的操作簡單話,不然每次都從幾百個菜單選項中選擇給該用戶授權的菜單權限,恐怕這個權限系統管理員就要崩潰了。:-),權限管理系統將權限分為了權限組,權限域,基本權限管理。 權限組和權限域的區別我至今沒有太分的清楚,郁悶中····在不多的使用當中(主要是設置測試用戶的權限)感覺上這兩種權限范圍設置是評級的,沒有誰是誰的上級的關系,不清楚當初為什么設置成這樣,我用的主要是權限域的設置。 給用戶授權下權限,前提是已經有相應的權限域設置。 1.權限域設置:權限域需要相應的設置編碼,并選擇該權限域包括的菜單權限。 2.在設置好權限域后,就可以直接將該權限域授權到系統用戶。 權限管理使用了數據庫信息管理,現在使用起來比較笨拙,不過由于最初的設置過于復雜,如果使用現在比較統一的xml文件配置,可能使用起來也不是很方便。 每個菜單權限項都有一個自身的權限編碼,該系統的菜單全部分為三級,編碼規范為:一級菜單編碼_二級菜單編碼_三級菜單編碼。這樣,在授權權限的時候可以直接將一級菜單授權,用戶相應的也獲得該一級菜單下所有功能的使用權。 用戶登錄成功后系統會將該用戶的合法權限值壓到一個集合中,放到sessin對象里。 權限的控制在系統的頁面和后臺控制類都有實現。頁面使用了上一篇提到的頁面邏輯標簽實現控制,在顯示該頁面之前,控制類使用了個系統框架提供的公共權限控制類將該用戶的權限值壓到頁面值中。 在調用該功能之前,控制類現會判斷該用戶的權限,有一個通用類,將該功能的權限和當前的用戶對象作為參數傳入,該類會判斷在用戶的權限集合中是否包括該權限,以決定是否繼續執行該功能流程。 這個系統的權限管理部分就是這么多了,下一篇講講這個系統的數據庫管理部分。維護的這個軟件系統使用了一個比較復雜的系統權限管理系統,原本的設計是將用戶對軟件的使用權限控制到以最末級的菜單,并根據公司組織機構的設計,上級組織單位可以將自身所獲得的權限繼續授權到自己的下級公司。 為了授權權限的操作簡單話,不然每次都從幾百個菜單選項中選擇給該用戶授權的菜單權限,恐怕這個權限系統管理員就要崩潰了。:-),權限管理系統將權限分為了權限組,權限域,基本權限管理。 權限組和權限域的區別我至今沒有太分的清楚,郁悶中····在不多的使用當中(主要是設置測試用戶的權限)感覺上這兩種權限范圍設置是評級的,沒有誰是誰的上級的關系,不清楚當初為什么設置成這樣,我用的主要是權限域的設置。 給用戶授權下權限,前提是已經有相應的權限域設置。 1.權限域設置:權限域需要相應的設置編碼,并選擇該權限域包括的菜單權限。 2.在設置好權限域后,就可以直接將該權限域授權到系統用戶。 權限管理使用了數據庫信息管理,現在使用起來比較笨拙,不過由于最初的設置過于復雜,如果使用現在比較統一的xml文件配置,可能使用起來也不是很方便。 每個菜單權限項都有一個自身的權限編碼,該系統的菜單全部分為三級,編碼規范為:一級菜單編碼_二級菜單編碼_三級菜單編碼。這樣,在授權權限的時候可以直接將一級菜單授權,用戶相應的也獲得該一級菜單下所有功能的使用權。 用戶登錄成功后系統會將該用戶的合法權限值壓到一個集合中,放到sessin對象里。 權限的控制在系統的頁面和后臺控制類都有實現。頁面使用了上一篇提到的頁面邏輯標簽實現控制,在顯示該頁面之前,控制類使用了個系統框架提供的公共權限控制類將該用戶的權限值壓到頁面值中。 在調用該功能之前,控制類現會判斷該用戶的權限,有一個通用類,將該功能的權限和當前的用戶對象作為參數傳入,該類會判斷在用戶的權限集合中是否包括該權限,以決定是否繼續執行該功能流程。 這個系統的權限管理部分就是這么多了,下一篇講講這個系統的數據庫管理部分。 |
這個軟件架構使用的mvc架構包括使用自身一套頁面邏輯,標簽類型比較簡單,包括邏輯判斷標簽、頁面賦值標簽和循環list對象取值標簽。
在執行完頁面請求返回的頁面沒有規定,可以是jsp,也可以是html頁面,使用上一篇提到的Common對象跳轉頁面,方法為:common.showPage("page")方法,因為沒有該框架的這部分源代碼,只能大概推測,這個方法是讀入頁面,并將執行頁面含有的邏輯標簽和賦值,一個方法可以多次使用該方法,最后的執行結果是多個頁面順次顯示,比如: ![]() ![]() ![]() ![]() ![]() page1的頁面代碼: ![]() ![]() ![]() ![]() ![]()
page2的頁面代碼:
![]() ![]() ![]() ![]() ![]()
最后顯示的頁面代碼:
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 頁面的邏輯標簽很簡單:<if><else><endif>,完成簡單的頁面邏輯判斷 在頁面最終生成之前執行的代碼,common的showpage方法會將標簽作為java代碼執行。 頁面賦值標簽寫法很簡單:($value$),value為要獲取的參數值,使用$符號區隔,可以和邏輯標簽混合使用: <if ($value$)==1>1<else>2<endif> 寫法有些類似現在流行的mvc開源框架。 另一個常用的循環標簽:<loop ($list$) ></loop> list為循環的集合對象。也可以同時循環多個list集合對象,但前提是多個對象的集合大小相同,不然就會出現數組溢出的錯誤。 該mvc框架配合了一套js腳本庫一同使用,因此像一些常用的腳本方法就和mvc框架的一些特點結合起來使用,js代碼編寫量減少。 表單提交參數驗證有一套統一的方法,現在應該應用的很普遍了。在表單對象標簽里增加一些屬性,最后提交表單是調用統一表單驗證方法,該方法會讀取該對象的屬性,判斷該對象值是否符合要求,包括判斷復選框對象是否有選擇對象,例如在刪除列表中的多個對象使用。 在常用的查詢列表和該列表的某個具體對象時,腳本庫提供了統一的方法: 1、常用的翻頁方法,首頁,尾頁,上一頁,下一頁,跳轉到某一頁的方法,只需要使用腳本庫中統一的方法,傳入表單名稱參數,class名稱,method名稱,跳轉到某一頁需要再傳入頁面參數即可,不需在編寫js代碼。 2. 查看某一條具體記錄,方法類似于翻頁方法,同樣傳入表單名稱參數,class名稱,method名稱表單名稱參數,class名稱,method名稱,和該條記錄的主鍵值。 今天寫到這里,下一篇寫寫這個系統的權限系統。 |
今天總結一下這個系統軟件使用的系統架構。 這個軟件使用的mvc框架有點類似于struts,我是在2005年開始接觸java的,在維護這個系統軟件之前使用的mvc框架包括struts和webwork兩種,相比較之下,與strtus更為類似,或許也是因為strus中控制類部分也是直接操作request吧。 這個mvc框架自身的特點大致有這么幾個: 1.這個mvc框架沒有使用配置文件,在webconfig文件中配置有一個總控的servlet,負責接受頁面提交的請求,進行轉發。 頁面提交請求方式是直接請求這個總控servlet,在提交請求到這個servlet的同時,提交兩個固定的參數,分別是class和method參數,通知servlet將請求轉到相應的類,并調用參數中的方法。 2.總控servlet調用的類為普通的java類,沒有特殊的要求,比如struts中要繼承action父類,方法寫法有嚴格的要求,為靜態方法,不允許有返回參數,接受的參數也固定,如下: public static void expMethod(HttpServletRequest req, 因為沒有使用配置文件,方法調用結束后返回到哪個頁面,在該方法中直接賦值該頁面的具體路徑。這樣修改起來有點復雜,需要重新編譯類,并且在調試的時候,如果需要找到該請求調用的頁面的具體路徑,必須找到這個類和方法,不如使用配置文件一目了然。 3.獲取頁面上的參數,這個框架提供兩種不同的選擇。 框架提供一個Common的對象,該對象封裝了對request的部分操作,提供了一個RequestHash對象,該對象為一個Vector集合,頁面表單提交的數據已經封裝到該集合中,參數獲取方式為,創建一個字符串數組,將需要獲取的參數名稱賦值到該數組中,Common的toData方法返回與字符串數組相對應的參數值,如下 Common common = new Common(req,res);//req request對象,res為response對象 String[] keys = {"param1","param2"}; 此時values數組中,values【0】中的值為param1表單值,相應的為param2的表單值。 如果需要獲取一個數組值,request的操作是getParamValues方法,例如表單中的復選框值,RequestHash對象提供了類似于request的這個操作。 reh.getParameterValues("params"); 4.為顯示頁面賦值也使用了Common對象。 a、 賦值簡單的字符串對象:common.addString("showParam", param); 第一個參數為頁面獲取該參數值使用的名稱,第二個為該對象名稱。 b、一次性賦值多個字符串對象:common.addArray(keys, values); 相對應的,keys為頁面獲取參數值的名稱,values為參數的值。 c、賦值一個復雜的對象,例如自定義對象或者一個List對象: common.addObject("voname", vo); 用法一致。 該框架提供一套通用的js腳本庫和一些統一的賦值規則,例如登錄用戶對象名稱,子系統名稱和提示名稱,因此common對象賦值也提供了一些特殊的方法,只接受參數的值,參數名稱為已定義名稱,因此在為頁面賦值也要注意不要與系統框架已定義對象名稱沖突。 今天寫到這里,下一篇寫寫這個mvc框架的頁面邏輯,標簽還有這個腳本庫的內容吧。 |