2006年5月22日
應朋友邀請,周六早從上海出發往杭州參加阿里巴巴網俠大會。同行四人,有銳道的macro chen、楊光(還是我師弟)、移動的王偉旭(特長是linux和網絡安全,也是中國linux推廣的先驅)。一路上,言談甚歡。老莊給我們訂的票,他一早腸胃有恙,仍然堅持把票送到火車站,之后去吊鹽水,下午又出現在會場。確實精神可嘉,建議阿里巴巴頒發“最佳精神獎”。
到杭州已是中午,錯過了上午大會。下午Robbin進行Java技術展望和RoR實現REST的演講,既然是朋友,肯定是要捧場的。Robbin旁征博引,以其深厚的技術功底和對新技術的敏銳洞察贏得了聽眾。
晚上一堆人去聚會,各路豪杰紛至:有阿里巴巴的,有自己創業的,有技術大牛,還有媒體(Infoq),出版社(博文的周總領3員大將赴會)。大家互換名片,認識的不免寒暄幾句,不認識的也很快就熟捻了,還不時有“原來你就是×××”的驚呼,原來網上就“互通心曲”,只是一直沒機會認識罷了。
席間觥籌交錯,具體內容暫且不表,只說一件令我感受頗深之事。一個阿里巴巴的員工表現出對公司的無比忠誠,講起公司的獎懲制度,說是一個員工的績效不僅跟所在項目相關,還與部門、其它部門甚至整個公司的業績相關。所以只要是對公司有利的事情,即使與自己現在的工作無關,他們也會去做。按常理來說,這有點不公平,我只能努力做好自己的事情,而如果別人不努力,我就是白做。但如果大家都努力,又變成了共贏。
這里讓我講一個簡單的博弈問題,就是“囚徒困境”。A和B兩個同犯被抓,因為沒有其它任何證據和證人,只能讓2人分別交供。如果A和B都矢口否認,那么兩人無罪釋放。如果A承認,B不承認;A是坦白從寬,判1年;B抗拒從嚴,判5年,反之亦然。如果2人都承認,ok證據確鑿,各判2年。如果2人都是理性人,且沒有互通消息,按照博弈,每個人的最優解就是承認,也就是各判2年。其實對2人真正有利的就是打死不承認然后都無罪釋放,而這種狀態在理性人的假設下是很難實現的--除非有一個教父,一直灌輸他們不要出賣同伙。
馬云就是這個“教父”!
卡內基有篇文章,我總結成一句話就是:用崇高的理想打動別人。據說馬云一直是以個人魅力及“創造中國電子商務的明天”類似的理想,激勵員工的。有了統一的企業文化,員工都不計較個人得失,努力奮進,最終企業和所有員工取得共贏,這絕對是擺脫“囚徒困境”的典型案例。
話說回來,阿里巴巴能讓你感受到團隊的力量,一群精英在一塊做很有價值的事情,對每個人也是很好的鍛煉。個人認為,如果有吃苦耐勞的打算,眼光放長遠點,又沒有其它方面的束縛,阿里巴巴的確是不錯的選擇。(得向阿里巴巴收代言費,呵呵!)
第二天聽了多場論道,主要是SAAS,搜索,分詞方面。結合阿里巴巴的戰略,我把幾點融合起來講一下。這個下篇再細細道來。
這次給
openfans
做網摘功能,主體程序倒是很快就寫完了,另外要做個
IE
插件,卻碰到了不少問題。
IE
插件其實很簡單,就是用
js
獲得頁面的標題、
url
和選擇的內容,然后通過彈出窗口,將其送到服務器。這里就有中文的問題了,開始使用
escape
,如
escape(title)
形式,
request.getParameter
碰到中文就為
null
,網上搜了一通,說是可以通過
java
編碼搞定,但拿到就為
null
了,還怎么換編碼?忙活了好幾個小時,又是
alert
,又是
document.write
,看上去也沒什么問題。不
escape
,直接在瀏覽器中輸入帶中文的
url
,拿到的不為
null
了,拿到后,通過
new String(str.getBytes("ISO-8859-1"), "UTF-8");
還真顯示正常了。但用
window.open
又出亂碼了。看到文章說還有
encodeURIComponent
方法可用,就試了下,把
escape
換成
encodeURIComponent
居然搞定了,服務端還是得用
new String(str.getBytes("ISO-8859-1"), "UTF-8")
進行處理。注意這里用的
tomcat
,它的默認編碼就是
"ISO-8859-1"
,如果改了編碼程序也得做相應的改動了。
今天為了在本機裝個wordpress玩玩,搞了搞php5+mysql5+apache2。網上搜了一篇文檔,很快就讓php與apache跑起來了,但連mysql始終不行。報錯:Call to undefined function mysql_connect()。查了一下半天,就是php關于mysql的ext沒配好,但我改了php.ini啊,也把"extension=php_mysql.dll"放出來了。查了好久,看到一篇說php5需要加上"extension=php_mysqli.dll",試了下果然好了。
???? 然后需要以index.php作為默認的welcomefile(不知道怎么叫,web.xml里是這個),需要在"DirectoryIndex index.html index.html.var"后加上 index.php就行。
然后飛快的裝了phpmyadmin、dvbbs的php版。發現php應用的安裝的確很是方便,解壓,拷貝到htdocs下,馬上就能運行了,比java應用簡單的多,更別提復雜的要死的企業應用了。這點上java要好好向php學習啊。
項目需要,開始研究電子支付。國外的電子支付提供商,得好好研究它的文檔和api。全是e文,只能慢慢看了。
? 學習了下spring2.0。對openfans而言,有2個比較重要的改進。首先是aspectj的支持,可以方便的使用aspectj語法定義aspect和pointcut了,openfans準備在domain object的自動注入上和權限等方面使用aop。另外就是spring form標簽庫的引入,現在springmvc也有自己的標簽庫,以前自己給checkbox和radio寫的request.getParameter可以改寫了。
摘要: 應項目需要做了一個定時更新的
cache
框架,采用
spring+quartz
很方便的實現,可以適用任何需要定時才更新的地方,比如靜態網頁
cache
等。代碼很簡單:
---------------------------------QuartzCacheHandler-------------------...
閱讀全文
接著前面的寫。上文主要寫了
ajax
在
portal
中的使用,這篇寫集群方面的體會。現在比較流行的架構就是前端
F5
做負載均衡,后面
2
臺
websphere server
做成集群,各自都有
HttpServer
,每個
HttpServer
都向
2
臺
was
做轉發。這樣每臺都能獨立完成從
HttpServer
到
was
的流程。一臺出現故障,
F5
首先進行切換,只向正常
server
的
HttpServer
發起請求,這臺
HttpServer
再進行切換只向同一臺
server
上的
was
做轉發。這次
portal
就是采用的這種架構,不妨稱為架構
A
。
另一種簡單點的架構就是只做
F5
負載均衡,不做
was
集群,每臺
websphere server
上的
HttpServer
接受
F5
轉發的請求,只向本
server
的
was
轉發。這樣每臺
websphere server
保持獨立,相互間沒有數據交換和轉發。不妨稱為架構
B
。
架構
A
和
B
各有優劣,適合不同的需要,下面進行些比較:
?????????
從應用部署上看:
A
使用了
websphere
集群,由一個
DeployManager
進行分發,部署應用,只需部署一次,由
DM
分發到幾個節點上。而
B
每個
server
都是獨立的,部署應用只能一臺臺部署,如果
server
較少差別還不明顯,如果達到
10
臺以上,一臺臺部署將是一個比較痛苦的事情。
?????????
從
session
上看:
A
使用了
websphere
集群,可以使用集群提供的
session
復制,對于一些關鍵應用(某臺服務器宕機,
session
也必須保持的應用)很有必要。而對于一些能夠允許
session
丟失的應用,才可以使用
B
。當然
A
也可以關閉
session
復制,因為
session
復制不管是使用數據庫方式還是內存方式,總會消耗一定的性能。具體消耗多少性能,就要看不同的
application server
的
session
復制方案了,想深入了解,可以看集群方面的文檔,我也只記得一個比較簡單的
round robbin
了。
?????????
從架構復雜性看:
B
更為簡單,因為沒有
DM
的概念,每臺
server
都保持獨立。而使用了
DM
有時也會出現莫名奇妙的問題,這當然是由于不了解
DM
的機制所致,但總歸也增加了復雜度,這點在后面的教訓中進行說明。
?????????
從水平擴展性上看:
B
肯定更勝一籌。只要
F5
能支持,多少臺
server
都沒關系。而
A
多臺
server
做集群,要看
websphere
支持的節點數量,應該不會太大。這個如果哪位同學知道,敬請告知。
當然
A
和
B
在服務器較多的情況下是可以共存的,可以考慮幾臺機器做集群,然后集群間做負載均衡,這樣既可以減少部署的復雜度,又可以帶來較好的水平擴展。由于沒做過更大型的項目,這個也只是我的假象,請做過的同學斧正。
?
說一說集群中碰到的問題。
?????????
首先是對各節點的同步:
有時為了方便測試,我們只對其中一個節點進行更改,測試通過再放到其它節點。而如果測試周期較長,有時就會造成節點的不同步,出現各種各樣莫名其妙的問題。一個經驗就是:無論如何,在每天下班前要保證各節點的同步,不同步的現象不要過夜。
?????????
然后是對
DM
的理解:
我現在還只是實踐階段,沒有看過相關文檔。從意義上看,它控制了相關的配置文件,如果進行節點同步,就會由它把配置文件同步到它管理的節點上。這對配置文件的修改提出了要求。我們開始只修改節點的配置文件而沒有修改
DM
的,結果進行節點同步就會覆蓋修改的配置文件,帶來很多不必要的工作。經驗就是:或者修改
DM
的配置文件,然后進行節點同步,或者直接同時修改所有節點和
DM
的。
?????????
還有關于
cache
的:
Cache
是性能優化的一個有效手段。在單機環境下,最簡單的就是內存
cache
,使用
static
的
Map
就行。而在集群環境中,
cache
就變的比較復雜了。首先還是從應用需求入手,是否要保持每臺機器的
cache
同步。如果只是信息展示等要求不高的
cache
,不需保證
cache
的同步,問題也比較簡單,自己寫內存
cache
,或者使用開源的
cache
組件如
ehcache,oscache
等就可以很好的解決問題。而如果需要
cache
在幾個節點保持同步,就需要特殊的機制了,
ehcache
等號稱支持分布式
cache
,但好像需要
jgroup
,配置比較麻煩,我沒有用過,有用過的同學請指教。我本來想使用
session
保存,然后進行
session
同步,后來
IBM
建議使用數據庫
cache
,即自己寫代碼,
cache
在數據庫中。這樣不需要
session
同步,對象不大,性能也能得到保證,現在用下來效果還可以。
?
這次做
ibm
的
portal
,算是臨危受命。做了幾個月的
SA
離職,留下一個功能和性能都有很多問題的項目,臨時讓我頂上。經過一個多月的緊張工作(經常加班,上班上不了網,也沒時間上網),總算功能和性能上都能達到客戶要求了。而我也由一個不懂
portal
的人,經過項目中實戰,不說成為高手,一般的概念、開發、配置、優化等也都有了很多體會。
這次技術上值得推薦的就是合理的使用
ajax
,既加快了首頁的
load
速度,又帶來了很好的用戶體驗。開始首頁上所有
portlet
都是串行加載,有的
portlet
比如新郵件,依賴于
mail
系統提供的接口。開始這個接口在較大壓力下就出現性能瓶頸,后在我們的要求下替換了協議,性能也在
1s-2s
之間。如果采用常規的辦法,加上
wps
驗證、運算,顯示主題、皮膚,加載所有
portlet
,響應時間肯定在
10s
以上。
我在
openfans
中使用了
ajax
,有些經驗,所以決定采用異步加載:首頁
load
時一些
portlet
直接顯示正在
loading
的字樣,在
body onload
時再使用
ajax
填充內容;使用
iframe
的
portlet
,也是
src
先指向一個靜態的正在
loading
頁面,
body onload
時再替換
src
到實際地址(這是
ajax
模式的一種)。這樣首頁登錄實際上只經過
wps
內部的驗證和顯示,所有業務邏輯都是加載成功后再并行進行。實際表現效果就是:頭上的主題很快出來,一塊塊區域顯示正在
loading
字樣,性能快的
portlet
很快出來,需要幾秒的
portlet
隨后出來,而不是讓用戶傻等
10
多
s
再一下全部顯示。
使用
ajax
同時也能解決頁面刷新問題和獲取返回值的問題。比如前面顯示新郵件的
portlet
,用戶點擊了一封郵件,新郵件數應該減
1
,剛點擊的郵件也應該上頁面上消失。原始的做法就是刷新整個頁面,既加大服務器壓力,又帶來很差的用戶體驗。使用
ajax
,在點擊后
1s
(或者更長,這取決于郵件系統對點擊操作的響應快慢)刷新
div
的內容,用戶甚至感覺不到內容已經更新。其它
portlet
也不需要重新載入,大大減輕服務器的壓力。有的操作需要提交給其它系統,而且可能成功可能失敗,這就需要獲得返回值。如果使用普通的
form
提交,需要更新整個頁面。而使用
ajax
提交,可以方便的獲得其返回值,進而顯示不同的提示。
另一個架構上的特點就是
portal
服務器職責單一
。開始所有的業務邏輯都是寫在
portlet
里,加重了
portlet
服務器的壓力。我進來后做的一個大的規劃就是,把業務邏輯抽離到其它
server
上,然后通過
ajax
加載到
portlet
中。這樣既可以充分利用服務器資源(新的
server
使用單獨的內存空間和線程池),又使得
portal
服務器職責更單一:僅進行驗證、權限控制、主題、皮膚和
portlet
的展示。
先寫這么多。因為使用了
2
臺
server
做集群,在分布式環境下,開發也有了更多的要求(比如
cache
),后一篇文章再細細道來。
難得有空,寫篇程序之外的文章,關于壓力的,也是自己近來的親身體會。
眾所周知軟件這行壓力是很大的。各種各樣的問題層出不窮,每天上班工作內容都是排的滿滿的,遇到突發問題就得加班。如果不及時進行疏解,積累到一定的程度,就可能產生一定的負面問題,比如上班精神狀態差、注意力不能集中、遇事喜歡逃避等等。我就親身經歷了這樣的狀況,明知自己工作積極性差、效率很低,但也很難一下子找回自我。
一次偶然的出游讓我從中很快走了出來。一個親戚考上廈大的博士,我請了
2
天的假,利用周末時間順便去廈門旅游。廈門依山傍海,的確是旅游的好去處。晚上到海邊,涼風習習,光腳沿著沙灘走過,任起落的潮水在腿上腳上留下層層薄沙。內心也變得平靜,能夠感受到海的呼吸。天地間仿佛只剩下我和大海,在進行心靈的交流,俗世煩擾皆拋諸腦后,只剩下對海的依戀。白天去爬南普陀山,并不太高,慢慢爬到山頂,整個思明區盡收眼底,遠處一艘快艇在海面掠過,留下一條美麗的浪花。然后順山而下到植物園,途徑無數奇花異草、層天老樹,走得累了,找個湖邊石凳休息一下,人也覺得輕松愉快。
經過大自然的洗禮,回到單位,人的精神面貌煥然一新,抱著積極的心態處理事情,很多問題迎刃而解。壓力測試做的很累,經常要熬夜,但通過一輪輪的測試,逐步定位到性能問題所在,自己也學了不少相關知識,想想也就沒那么煩了。
做事的方式,也有了長進。我現在信奉人一時只做一件事效率最高的原則。事情再多,也是一件件做,每天安排好近日的工作,并排個優先級,什么是要親自處理的,什么是讓別人處理的,什么是需要預先通知他人的,需要什么資源,每件事情的預計時間如何,需要如何
check
等等。做好一件事就打個勾,做到心中有數。如果事情有延誤,分析是什么原因,該如何補救,而不要有太大的心理負擔,自己盡力了就好,是自己的責任就要勇敢扛下,死不了人的。這其實是很簡單的原則,誰都能夠學會,但的確很管用。
總結:壓力是無處不在的,關鍵在于如何應對和排解。用積極的心態和恰當的方法面對,壓力也就沒那么大了。感覺壓力積累到一定程度,在還未影響正常工作之前就先想辦法排解,出去旅游、運動等都是緩解壓力的好辦法。
最近對項目組的一些較差的代碼進行了些重構,同時靈光一閃,對代碼有些比較形象的比喻。
壞的代碼就象揉面團,管什么接口什么實現全揉成一團,一個方法幾百行,注釋寫再多也是面團(夾了些小紙條而已)。然后需要重用了,就是從中抓起一把面團,然后放到其它的面團里繼續揉。這樣重復代碼一堆,什么易讀性、擴展性、可維護性都是無從談起。
好的代碼就象堆積木,接口實現定義清清楚楚,每個接口只做一件事情,重復代碼都是通過更細的接口來消除。重用就是把積木塊往該放的地方堆,這樣的代碼,幾個大塊幾個小塊一目了然,只要方法命名規范,連注釋都可以省去。這樣耦合性低,易讀性、擴展性、可維護性都可以得到保證。
把面團變成積木并不復雜,定義好模具,面團一團團往里面填充,待穩定下來,就成了一塊塊積木。這里關鍵就是模具的制作,推薦制作寶典:
martin fowler
的那本重構。還得有模具的丈量工具,就非
junit
莫屬了。
??? 項目需要寫了幾個數據庫同步用的
trigger
,就是記錄用戶的操作到一個
temp
表,然后每天通過
webservice
同步到其它系統,同步成功清空該
temp
表。自認為寫的還行,做個記錄。是
db2
的。
?
--
用戶組新增觸發器
--DROP TRIGGER TG_USERG;
CREATE TRIGGER LIBING.TG_USERG AFTER INSERT ON LIBING.TM_USERG
? REFERENCING NEW AS NROW
? FOR EACH ROW
? MODE DB2SQL??
? BEGIN ATOMIC
?
? declare @groupId integer;
? declare @name varchar(30);
? declare @descn varchar(100);
? declare @syntype varchar(4);
? declare @ddlsql varchar(1024);
? declare @isprimary char(1);
? declare @updateTime timestamp;
? declare @createTime timestamp;
? declare @createBy integer;
? declare @updateBy integer;
? declare @groupType integer;
? declare @adminType integer;
? declare @appId integer;
?
? declare @oldGroupId integer;
?
? set @groupId=NROW.GROUP_ID;
? set @name=NROW.name;
? set @descn=NROW.descn;
? set @syntype=NROW.syn_type;
? set @ddlsql=NROW.ddlsql;
? set @isprimary=NROW.isprimary;
? set @updateTime=NROW.update_time;
? set @createTime=NROW.create_time;
? set @createBy=NROW.create_by;
? set @updateBy=NROW.update_by;
? set @groupType=NROW.group_type;
? set @adminType=NROW.admin_type;
? set @appId=NROW.app_id;
?
? INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,
?
??????????
?CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,
?????????????
?@ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'INSERT');
? END;
?
? --
更新用戶組數據的觸發器
?-- DROP TRIGGER TG_USERG_UPDATE;
? CREATE TRIGGER TG_USERG_UPDATE AFTER UPDATE ON TM_USERG
?
??????????
?REFERENCING NEW AS NROW
?????????????
?FOR EACH ROW
?????????????
?MODE DB2SQL
?????????????
?BEGIN ATOMIC
?????????????
?
?????????????
?declare @groupId integer;
?
??????????
?declare @name varchar(30);
?
??????????
?declare @descn varchar(100);
?
??????????
?declare @syntype varchar(4);
?
??????????
?declare @ddlsql varchar(1024);
?
??????????
?declare @isprimary char(1);
?
??????????
?declare @updateTime timestamp;
?
??????????
?declare @createTime timestamp;
?
??????????
?declare @createBy integer;
?
??????????
?declare @updateBy integer;
?
??????????
?declare @groupType integer;
?
??????????
?declare @adminType integer;
?
??????????
?declare @appId integer;
?????????????
?
?????????????
?set @groupId=NROW.GROUP_ID;
?
??????????
?set @name=NROW.name;
?
??????????
?set @descn=NROW.descn;
?
??????????
?set @syntype=NROW.syn_type;
?
??????????
?set @ddlsql=NROW.ddlsql;
?
??????????
?set @isprimary=NROW.isprimary;
?
??????????
?set @updateTime=NROW.update_time;
?
??????????
?set @createTime=NROW.create_time;
?
??????????
?set @createBy=NROW.create_by;
?
??????????
?set @updateBy=NROW.update_by;
?
??????????
?set @groupType=NROW.group_type;
?
??????????
?set @adminType=NROW.admin_type;
?
??????????
?set @appId=NROW.app_id;
?????????????
?
?????????????
?--
如果已經有
update
則只記錄最后一條
update
?????????????
?IF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='UPDATE') THEN
?????????????
?
???? UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,
?????????????
?
????
?
??????????? NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,
?????????????????????????????????? ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,
?????????????????????????????????? CREATE_TIME=@createTime,CREATE_BY=@createBy,
?????????????????????????????????? UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,
?????????????????????????????????? ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='UPDATE'
?????????????????????????????????? where GROUP_ID=@groupId AND ACTION='UPDATE';
?????????????
?--
如果有
insert
則把后面的
update
當作
insert
????????????? ELSEIF? EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='INSERT') THEN
???????????????????? UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,
?????????????
?
????
?
??????????? NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,
?????????????????????????????????? ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,
?????????????????????????????????? CREATE_TIME=@createTime,CREATE_BY=@createBy,
?????????????????????????????????? UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,
?????????????????????????????????? ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='INSERT'
?????????????????????????????????? where GROUP_ID=@groupId AND ACTION='INSERT';
????????????? ELSE????? INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,
?
??????????
?
???????????
???CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,
?????????????
?
???????????
?? @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'UPDATE');
????????????? end if;
?????????????
?END;??????????
?
?
--
刪除用戶組觸發器
--DROP TRIGGER TG_USERG_DELETE;
CREATE TRIGGER TG_USERG_DELETE AFTER DELETE ON TM_USERG
??????
? REFERENCING OLD AS OROW
??????
? FOR EACH ROW
??????
? MODE DB2SQL
??????
? BEGIN ATOMIC
??????
?
??????
? declare @groupId integer;
?
???
??declare @name varchar(30);
?
???
??declare @descn varchar(100);
?
???
??declare @syntype varchar(4);
?
???
??declare @ddlsql varchar(1024);
?
???
??declare @isprimary char(1);
?
???
??declare @updateTime timestamp;
?
???
??declare @createTime timestamp;
?
???
??declare @createBy integer;
?
???
??declare @updateBy integer;
?
???
??declare @groupType integer;
?
???
??declare @adminType integer;
?
???
??declare @appId integer;
?????????????
?
??????
? set @groupId=OROW.GROUP_ID;
?
???
??set @name=OROW.name;
?
???
??set @descn=OROW.descn;
?
???
??set @syntype=OROW.syn_type;
?
???
??set @ddlsql=OROW.ddlsql;
?
???
??set @isprimary=OROW.isprimary;
?
???
??set @updateTime=OROW.update_time;
?
???
??set @createTime=OROW.create_time;
?
???
??set @createBy=OROW.create_by;
?
???
??set @updateBy=OROW.update_by;
?
???
??set @groupType=OROW.group_type;
?
???
??set @adminType=OROW.admin_type;
?
???
??set @appId=OROW.app_id;
??????
?
??????
?? --
如果沒有操作記錄,則插入
delete
記錄
??????
?? IF NOT EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId) THEN
??????
??
?
??INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,
?
??????????
?CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,
?????????????
?@ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'DELETE');
?????????????
?
?????????????
?--
如果有
insert
記錄,則整體結果相當于沒有進行任何操作
?????????????
?ELSEIF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT') THEN
?????????????
?
??????????? DELETE FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT';
?????????????
?--
如果沒有
insert
記錄,則只需記錄最后的
delete
操作
?????????????
?ELSE
?????????????
?
????
? UPDATE TM_USERG_TEMP set ACTION='DELETE' where GROUP_ID=@groupId;
??????
?? END IF;
??????
??
??????
?? END;
在項目中碰到一些重用上的問題,有些想法,就先寫一點。
重用應該是高層的復用,邏輯的復用,接口的復用,而不是具體實現的復用。
我們項目開始講復用,就是大家把別人的代碼拿過來,可用的地方就用,不同的地方改改,結果問題一堆。說到底就是接口沒有定義清楚的,很多該復用的邏輯隱藏在了具體的實現中。這樣導致無法進行接口的復用,轉而使用具體的實現復用。從程序員的角度看,他們總會使用成本最小的方法完成任務。所以我們要時刻思考如何能讓最正確的方法在他們看來同時也是成本最小。
這里有一個較為簡單的辦法,就是盡量使用方法封裝實現,使接口的粒度最小。如果一個實現需要幾百行,且其中包含多個邏輯,就最好抽取出多個方法,然后在主體接口內進行調用。這樣的代碼邏輯清晰易讀,可重用性也高。看看大師們對代碼的不斷重構,很大程度上就是重構出粒度最細,復用性最高的接口。
如何達到最大程度的復用,其實是非常復雜的問題,還需要在今后的項目中不斷體會。
最近做
portal
的壓力測試,一個字“累”。其中犯了不少錯誤,白白加了幾天班,也有一些體會,就記錄下來,希望對大家有所幫助。
首先講壓力測試環境。這個很是關鍵,我們就是在這個上面吃了苦頭。我們用的
loadrunner
,原理也很簡單,一臺主控機,控制多臺客戶機,模擬并發用戶訪問應用。然后需要能實時監控各相關應用服務器,
ldap
服務器等的性能。這里每臺客戶機最好能使用同樣的配置,使用足夠帶寬的網絡,給予同樣的負載(模擬同樣數量的用戶)。同時要注意監控客戶機的
cpu
和網絡狀況,時刻保證
cpu
和網絡利用率低于
100%
。我們犯的很大錯誤就是使用各自的筆記本,而且都使用的是一個
10M hub
牽出的網線,這樣導致實際的網絡阻塞,既沒有給予服務器足夠的負載,又導致報告的響應時間比實際更長,從而帶來了后續很多的無用測試。
然后講測試方法。用的較多的還是持續壓力測試,就是持續給予服務器一段時間的并發量(一般為
5
到
10
分鐘),然后看平均響應時間是否在可以接受的范圍內。這個“可接受”要視應用類型和實際的并發用戶而定,如何估計并發就要靠經驗了。對于
portal
而言,由于要與眾多的應用接口,如進行
SSO
,獲取數據等,有很大程度也依賴于其它應用的性能,其性能要求不會太高。我們測試首頁的性能,在放上全部的
portlet
的情況下,
100
個并發的平均響應時間就在
17s
左右,這肯定是不能接受的(
10s
只能算勉強可以)。接下來就是發現性能瓶頸,并嘗試進行優化了。
初步的發現瓶頸的方法也很簡單,通過對
portlet
的增減,發現最影響性能的
portlet
,然后不斷優化,直至達到可以接受范圍。發現瓶頸所在了,就得進一步確定是什么原因:是我們本身程序的問題,還是其它應用接口性能不佳等等。這里光靠猜是不行的,要講數據講事實,記錄時間日志就是簡單有效的辦法,我們對各個時間點打印了日志,比如
doview
方法的全部執行時間,
jsp
的載入時間,具體接口的執行時間等。有些接口可能在壓力較小的情況下性能不錯,而在大壓力情況下出現性能隱患,所以一定要在進行壓力測試后查看日志。我們就是這樣發現了性能隱患,同時更進一步對各方面進行優化,直至達到客戶可以接受為止。
由于不是專門的測試人員,很多地方都是實際項目中的體會,也沒什么理論基礎。有什么不對的地方,大家多交流。
接著昨天的寫。今天寫我認為的一個
javaEE
項目中應該提倡的做法。
1.??????
開發流程盡量簡化,采用迭代增量的模式,做適合項目需要的文檔。很多時候千言不如一圖,原型開發我認為也非常重要。
2.??????
采用成熟的框架,
ssh
組合或更多
full-stack
的框架如
seam
等都是不錯的選擇。如果一定要用公司的框架,至少
SA
要非常熟悉這個框架,在出現問題時要能快速的解決。
3.??????
對業務的分析做到越細越好,如果有條件讓更多的開發人員參與業務的分析,同時形成項目通用的業務語言(實在不行,精簡的
user story
也可以)。對于每個達成共識的業務都要能記錄下來,并能方便的進行查閱。業務模型和業務規則要始終與當前需求、代碼和數據庫保持一致。
4.??????
在團隊的建設上,需要更多的投入。不要為了節約成本,讓很多程序員老后面才加入團隊。一個穩定、團結、有沖勁的團隊能比松散而人數更多的團隊,完成的更快更好。然后要加強溝通,比如每天開個小的茶話會,大家交流下各自的工作情況,有什么困惑和疑難,提出來大家一起解決,避免大家各自做相同的邏輯(很多東西經過抽象可能就是一個)。在工作之余大家一塊吃吃飯,打打游戲等都是增進感情的好方法,大家彼此熟悉了,工作上也能更好的協作。
5.??????
對程序員要有更高的要求,
SA
有責任讓程序員了解更多的東西,如面向對象的
5
大原則、一些模式、
junit
、重構等,這些其實并不是什么高深的東西,僅僅是掌握一些方面也能對代碼質量和開發中的愉悅度產生很大促進。要激發他們對技術的熱愛和對代碼質量的追求,因為最終受益的還是他們。
XP
所提倡的結對編程也是快速進行知識傳遞的好辦法。
6.??????
采用
wiki
進行項目進度跟蹤和一些文檔的展示。這次用
excel+cvs
的方式感覺很是麻煩,在
spring
翻譯中我們采用
wiki
的方式就感覺很好。
暫時先想到這么多,有更多體會,再來補充!
摘要: 在程序員發表的一篇maven文章,跟大家共享。用
Maven
做項目管理
在
Java世界中我們很多的開發人員選擇用
Ant來構建項目,一個
build.xml能夠完成編譯、測試、打包、部署等很多任務,但我們...
閱讀全文
開發進行到尾聲了,但
bug
仍然層出不窮。總的來說,算是一個比較失敗的項目,原因很多,有外在因素也有我作為一個
SA
不可推卸的責任。正好借加班的時間寫點總結,也算是在失敗總吸取教訓,從錯誤中感受更多吧。
首先是開發流程。我是
xp
的堅定支持者,但在項目中由于外界原因還是采用了傳統的開發流程,沒有迭代,就是需求
->
設計
->
程序員進場開發
->
改
bug
。由于程序員進場時間較晚,一上來就開始開發,沒有時間進行培訓和團隊的融合。然后開發中缺少溝通,就是一個人負責一塊,開發完了再做其它。結果開發到現在,還有人不清楚我們項目的全貌,到底是為了解決什么業務。
然后是開發框架。使用了公司的框架,而我們作為
SA
(我們是雙
SA
),都是第一次接觸,程序員也就一個人用過。我們最早是達成共識采用
SSH
的組合(我至少還算是了解吧,其它人也都用過),但由于上層因素沒有實施(這也導致我好長一段時間進入不了狀態)。開發前期大家都在探索這個框架(的確很難用,出錯機制較差,配置文件很多,耦合較強
...
),在一堆莫名奇妙的問題中摸索前行,花費大量的精力。而比較搞笑的是,在大家開始學習這個框架之時,我作為
SA
,因為要寫一堆只為應付客戶的設計文檔(后面就沒人看過),錯過了和大家共同進步的機會,后面總是感覺“低人一等”。
在業務方面也存在很多問題。很多業務邏輯并沒有以很好的載體保存下來,在需求文檔中很多邏輯并沒有體現。我維護了一套
pd
的業務模型,從概念模型
->
物理模型
->
數據庫,這解決了后面的一些溝通問題,但由于更多體現的是靜態的實體及關聯,對于一些動態的業務流程沒法體現。我們
SA
之間有時在一些問題上的理解還存在分歧(討論過也達成過共識,但沒有記錄下來,后面可能就忘了),程序員就更是無所適從。談到這,我更感受到
DDD
這本書的價值,他所提倡的開發人員參加模型的討論,形成項目的模型語言,并不斷隨著業務進行演化。。。好多理念都是項目經驗的結晶啊。
在開發管理上我也是無所作為。
Junit
都沒有推廣下去,更別說
TDD
了,這也與框架相關,它就沒提供寫
test case
的地方,等我搞明白一堆配置文件,做出脫離
web
容器的
test
框架,都開發一大半了,說起
test
的好處,大家也表示不理解(或者表示理解但沒時間
=
沒理解),就讓他們慢慢
debug
吧!代碼的質量也沒有保證,程序員不明白代碼的味道,更別說理解重構的意義以及進行恰當的重構了。一個函數寫上
100
多行,什么邏輯都混在一塊,但由于時間較緊,我也只好睜一只眼閉一只眼“功能完成就行吧!也不是我一個人在管”,到現在很多代碼混成一團,展現層直接調用
dao
(又是框架惹得禍),相同的邏輯
copy
到
n
處,我也是后悔莫及。
今天先寫失誤,明天寫從中學到的東西,從錯誤中學到的也許更多!
最近看了看領域模型驅動這本書,只看了前面幾章,但也深切的感受到了模型的重要性。通過與代碼同步的模型,能夠維護一個很好的知識共享的空間,包括設計者與程序員之間,客戶與設計者之間
……
而且模型應該盡可能簡單,讓不同背景的人都能夠很快學會,并都能對模型有所增益。
那么這個模型應該是什么樣的?書我沒有細看,只說說自己的體會。關于設計,很早就有數據驅動和對象驅動的提法。在
Without EJB
里,
Rod
也有講:數據驅動或者說面向數據庫設計更成熟,工具更多;而對象驅動更符合面向對象程序的特性,但由于掌握的人較少,風險較大。而通過模型驅動,我認為很大程度填補了
2
種方式的鴻溝,核心是模型,具體是對象模型還是數據模型并不重要,重要的是這個模型能夠與需求、代碼、數據庫保持一致。
說到這里,順便談一談我對文檔的理解。我一直是
XP
的堅定支持者,甚至有點偏執。而由于文檔不易閱讀和溝通,且經常會出現與設計和代碼的脫節,導致其可讀性更差,所以我一向對文檔不大感冒,更傾向于使用代碼說話。但在目前的公司項目中,由于更多采用傳統的軟件過程,我也寫了很多的文檔,包括需求規格說明書、概要設計文檔、詳細設計文檔等等。從對項目的幫助來看,文檔作用并不太大,或者說是付出收益比太低,更多的是給客戶寫的,而不是給程序員寫的。從程序員的需要來看,他關心的是每個實體的屬性和關聯,核心的接口、輸入和輸出,頁面間的跳轉和數據流,然后有一個統一的框架和編程模式。我的體會是:如果以文檔為核心,很難描述清楚這些東西,且難以應對變化。
而通過以模型為核心(項目現在采用的
power designer
的概念模型為基礎),輔以適當的描述,既能夠加快大家對項目的認識(程序員是后面才加入),又能夠節省一些寫文檔的時間,更早投入開發。
說到
power designer
,我也比較慚愧。用了好久,一直只是把它當成看數據庫的工具。項目一開始就是從物理模型入手,結果舉步維艱。后面從概念模型入手,就感受到了它的好處。使用概念模型,不用考慮太多關聯表、外鍵什么的,而是從實體出發,然后確定相互間的關聯,是一對一、一對多還是多對多。然后自動轉成物理模型,并直接與相應的數據庫掛鉤。從這點上看與從對象設計出發真的非常相似。其實這也是合情合理的,正體現了這個世界的統一性吧(物理學界不也在搞什么統一場理論的證明嗎)。
Power designer
也做了
conceptual model, physical model, object-oriented model
和
xml model
的自動轉換,我現在還沒全部摸熟。
openfans
則是從對象入手,并通過
hibernate
建立與數據庫的聯系,也體現了一定的方便靈活性。但比較糟糕的是,只有代碼和配置文件,沒有清晰的便于交流的模型,誰要想參與只能先去慢慢看代碼。所以我先通過
together reverse
出來一個類圖,然后適當加以文字進行說明。類圖已經做好,但比較亂,還需要更多的圖例加以說明。文字說明就是下一篇
blog
的工作了。也算是預告吧!
近來在一個項目做
SA
,也是第一次做比較大的項目的設計,感覺比較吃力。同時又要參與
spring
文檔的翻譯,一直沒時間寫
blog
。今天終于有點時間,就寫一下最近的感悟。
首先是不適應。要參與需求階段,因為需求初期并不確定,客戶都不清楚他們需要什么東西,只是有一個很模糊的概念。我們得不斷調研、討論、出方案、出原型
……
而這都是我比較不擅長的。還好有個職務較高的老大帶著我們,才能逐漸把需求理順。我也從他身上學到不少,準備寫一篇“如何做需求”,但畢竟是第一次做較大的需求,理解還不很深刻,怕貽笑大方,所以只拿
MindManager
列了個提綱。
其次還是不適應。項目開始好幾個月,沒寫過一行代碼。項目沒有采用
XP
的方式,而是普通的瀑布。需求就做了幾個月,然后做概設、詳設。我是
XP
的支持者,所以對這種方式持反對態度,但老大不同意,沒辦法!寫文檔,我也是很不情愿,但轉念一想:
Rod
寫
Without EJB
,但他
ejb
的理解比誰都深,什么方式都實踐下可能更好。由于同時在看
Joel on software
,他對需求規格說明書卻很是強調,我也就聽聽大師的話,好好寫需求,順便把他的一招用上了
-------
寫的有趣點,就當寫故事吧。
最后還是不適應。以前做程序員,可以好好研究很多東西,現在不行了。有個
xml
與
bean
轉換的技術要解決,我能研究不?不行,我得寫文檔,這種比較
detail
的事情得給程序員做。看著程序員興高采烈的比較各種開源工具,最后選定
JIBX
(
openfans
發揮了一定的作用),然后跟我講這個如何如何好,我只有附和的份。
講到這里,讓我想到一則小故事:有一個學鋼琴的拜一個牛人為師。牛人交給他一個曲譜,說:“回去練好,一個月再過來。”他好歹把這個曲練熟了,還想展示一下,牛人又交給他一個更難的曲譜,又是同樣的話。他只好回去繼續苦練,每次都感覺不適應。這樣往返多次,他忍不住了,問牛人:“你是不是故意整我,每次都給我更難的,還不給我表現的機會”。牛人讓他把上次的曲彈彈,他感覺不錯,讓彈再上次的,更是輕松,最后彈第一次,他彈的是出神入化。他明白了!
大家都明白沒:只有不斷的感到不適應,才能進步。如果一切感覺良好,沒什么挑戰,就該考慮。。。。。。(此處省略
2
字)了。
不經意看到了程序員的一期算法專題,細細研讀多位高手(包括李開復)的文字之后,對算法的重要性重新進行了反思。我研究生畢業
2
年,一直從事
J2EE
開發,由于項目的原因,很少需要自己去設計算法,甚至
stack
,
tree
這些數據結構都很少使用。還好自己也不甘于平淡,如
Effective Java
,
Practical Java
,
Refactory
,
Design Pattern
等等這些流行書還是抽空學習,這些書的確很是經典,對我的編碼風格,模式的理解,設計能力都起到了很好的促進。也快速的由一個程序員成長為架構師(只是公司的,離真正的架構師還差得遠)。
因為項目需要,去年下半年開始全面接觸開源軟件,使用了
spring
,
maven
,
hibernate
,
ibatis
等眾多開源軟件,也對開源軟件產生了濃厚的興趣,于是拿這些開源軟件做了
openfans
,一方面是推進開源軟件在中國的使用的交流,一方面也為自己在實踐中更多使用這些軟件(因為沒有項目和利益因素,可以做想做的事,用想用的軟件)。使用這些開源軟件倒很是順利,很多軟件拿來就能用,都有
sample
,簡單使用還是不難的。
但一些關鍵的問題一直懸而未決!比如
tag
的設計:我現在簡單的使用平鋪的模型,
tag
沒有層次之分,
tag
間產生雙向關聯。但這樣是最符合
tag
特性的模型嗎?如何對這些
tag
進行分類,如何定義
tag
的多級關聯(如
spring
和
hibernate
有關聯,
hibernate
又與持久層關聯,
spring
是否與持久層有間接關聯,依次類推)。。。。。。而做出一個好的
tag
模型,可能就需要圖論方面的知識。再比如用戶相似度設計(號稱是豆瓣的核心,難以復制):每個用戶擁有了一些
tag
,如何根據這些
tag
定義用戶的相似度,一個用戶有
spring
,
hibernate
這
2
個
tag
,一個用戶有
spring
,
ibatis
這
2
個
tag
,他們相似度為多少,如果每個人
tag
都很多,再加上權重的概念,問題又復雜的多。簡單的做法就是每個用戶
tag
一個個匹配,匹配的越多相似度越大,但這樣設計一是不準確,二是時間復雜度很大,最壞情況為
n*n*m*m
,
n
為用戶數,
m
為每個用戶的
tag
數。
這些都需要扎實的算法基礎。而我的基礎就很薄弱:本科學的比文科還文科的專業,研究生又學的比較上層的東西(
UML
,
RUP
,
PM
等,也都一知半解),選修了一門算法導論,又被
1000
多頁的經典英文教材嚇趴下了,上了幾次課就直接放棄,沒敢參加最后考試。現在想臨時抱佛腳,談何容易。
所以算法也并非沒有用處,關鍵要看你在做什么,想做什么。想去
google
、百度不用會
spring
,算法基礎扎實,只會
c
語言都行;一些行業如電信、金融也很是需要算法高手。而國內更多的企業做企業應用,一般是連連數據庫,寫寫頁面,最多引入些開源框架和軟件,如
spring
,
hibernate
,
struts
等。這方面的需求較大,會了
spring
,省了公司的培訓成本,自然還是給找工作加了一些砝碼。
所以有時聽到某些人對某項技術不以為然,說“這些東西有什么是我在幾個星期學不會的”的時候,一方面是對其狂妄進行些鄙視,一方面也真要問問自己,我的核心價值到底在哪。這個問題很重要,涉及面很廣,選擇也很多,而我也只是有些模糊的答案,等以后再仔細寫寫。
不管如何,我是要開始研究算法了,得解決問題阿!先在
openfans
開個算法的
tag
,一邊學一邊積累,對算法有興趣的同學也可以跟我一塊進步。
PS
:做個廣告,
blogjava
很多好的
bloger
,能否到
www.openfans.net
導入下
blog
,跟大家分享下你的感悟,謝謝!
很不好意思,不是原創技術。做個廣告,有不妥,歡迎管理員從首頁拿掉。
??? 你是開源軟件的愛好者,平時學習和使用這些軟件,也不時寫寫
blog
,記下些心得。
你是開源軟件的傳播者,你希望更多的人了解和使用開源軟件,希望你的文章被更多的人閱讀,并展開更深刻的討論。
你是開源軟件的參與者,平時參與參與國外的開源項目,也希望中國能有更多的開源團體,大家一起做國人自己的開源軟件。。。。。。
只要你對開源軟件保持著一份熱愛,歡迎來到
openfans(www.openfans.net)
。
非常方便的注冊后,你就可以點擊“提交
feed
”,只要輸入你的
rss
地址(由于時間原因,還沒做直接從
web
地址發現
feed
),就可以將你的
feed
加入,同時我們對一些網站提供了簡單的匹配(如
blogjava
,只需輸入你在
blogjava
的用戶名,系統會自動匹配成你在
blogjava
的
feed
)。完成后,點擊“立即導入”,就可以將你的文章入庫,點擊“最新日志”可以查看。以后系統會每日定期讀取你的
feed
,自動將新的文章加入。由于你提供的是
rss
,內容應該是文章的簡短描述(視你的
blog
提供商而定),而且我們會為每篇文章提供原文鏈接,直接指向你的
blog
原文。
導入的日志一般是沒有進行分類的,不方便大家的查找。在每篇日志上都有個“我要推薦”鏈接,點擊并輸入你認為適合的標簽(如
spring
,
hibernate
,
cms
)等,就可以把這篇日志形成文章,放在相應的標簽下,永久保存。需要學習
spring
,
hibernate
,
cms
的后來者,可以方便的查找到標簽和軟件,找到你的文章,進而進入你的
blog
。
同時你也可以發表文章,推薦軟件,創建和加入小組,進行評論。。。。。。我們會不斷完善功能,給大家提供更方便的功能和更好的用戶體驗。
由于現在人員較少,開發進度較慢。但先做個廣告,下一步會做
digg
,提供對軟件、文章、用戶等的
digg
。做對一些標簽的
rss
,如
springframework
網站的
rss
,自動獲取
spring
的版本更新信息。還要完善小組功能和好友功能,給大家提供一個方便交流的平臺。
網站拿
java
的一堆開源軟件做成,同時本身也是開源軟件,希望參與的同學可以
email
給
pesome@gmail.com
,大家一塊為推動開源軟件在中國的發展做出自己的貢獻。
? 前面openfans用的JDK1.4,今天下決心換成1.5了。運行倒是好好的,在jetty下也沒有什么問題。一不小心點了下eclipse里我做的mvn eclipse:eclipse的External Tools,就開始maven了。停也沒用了,等著吧。結果報錯:D:\javaproject\openfans\main\src\org\openfans\domain\Group.java:[29,19] -source 1.3 中不支持泛型(請嘗試使用 -source 1.5 以啟用泛型)。看了看maven的bat,會自動使用環境變量配置的jdk,應該沒問題啊。還好我網上認識人多,想起alin用的jdk1.5,就問他怎么回事。發過來這個:
<
plugin
>
??????
<
artifactId
>
maven-compiler-plugin
</
artifactId
>
?????????????
<
configuration
>
?????????????????
<
source
>
1.5
</
source
>
?????????????????
<
target
>
1.5
</
target
>
?????????????
</
configuration
>
</
plugin
>
我一看就明白了,
mvn
時是用
1.3
給我編譯的,得告訴它用
1.5
。拷到
pom
文件中,再
mvn eclipse:eclipse
搞定。問題是很快解決了,同時卻留下了很多思考:
1.
技術沒有止境,做人一定要謙虛。
Maven2
我用的也算比較早,還曾經被白衣說是對maven2的推廣做了貢獻的,自己也頗以為然。而現在這個簡單的問題卻不知道了,還得google或問人解決。還好我一直比較謙虛(本身也沒啥可驕傲的資本),否則要狂被鄙視了。
2.
技術的推廣要不遺余力,好的東西要讓大家都知道。
Maven2
我也只是使用,了解并不深入(項目中碰到了的知道,沒碰到的就不懂了),但我是到處推薦,碰到個人就說這個好。這下很多朋友都知道了,也引入項目實踐了。一方面他們用的舒服,提高了效率,有點問題還可以向我這個所謂的maven2高手請教,我自是“知無不言,言無不盡”;另一方面,他們也許就碰到其它問題了,然后知道如何解決,在我碰到類似問題時,就可以向他們請教了。你看,多好的良性循環,想想都美滋滋的。
3.
多進行知識共享,大家的智慧比個人強。
這是從更高的角度看了,通過知識的共享,能迅速集合大家的經驗和智慧,讓個體更快的進行學習,少走彎路。你共享自己知識的同時,也能獲得別人的成果。如果你知道誰spring比較強,誰hibernate比較強,誰在用maven,而且碰到問題能看他們的文章或直接向他們請教,做起項目來是不是都安心的多。可能有人說有google,但google信息量太大,而且很多文章是處處轉載千篇一律,經常半天找不到東西。我是深有體會,所以想到做openfans,做一個知識共享的平臺,并做到去糟取精。現在還遠遠達不到要求,但我會努力的。
領域模型驅動(
Domain Driven Design
),很熱的名詞。
Openfans
,不太熱的網站。今天俺就借著很熱的
ddd
,給不太熱的
openfans
再造點勢。
Openfans
就不多介紹了,網站用
spring+hibernate
為核心的一堆開源軟件構建。有了
spring
的
IOC
和
hibernate
的
ORM
,打著
ddd
的旗號也就名正言順了很多。先聲明其實俺對
ddd
的理解也多是道聽途說,沒有什么系統的學習過,倒是和
Joe
阿牛討論過幾次,頗有受益,他對這個理解還是很深刻的。
言歸正傳,就講
openfans
現在經
ddd
思想改造過的模型。整體上看還是普通的三層架構體系:展現層、業務層、持久層。展現層用
spring mvc
,力圖做到只是展示相關,避免出現業務邏輯。再具體細分,就是
jsp
頁面只有展示邏輯,主要使用
jstl
完成顯示功能。
Controller
負責從頁面獲得參數、把數據傳回頁面、控制頁面流傳和調用業務層的接口。持久層使用
hibernate
,在設計上我不是按
dao
方式為每個對象建立相應的
dao
,也不是
ddd
推薦的每個
domain
一個
repository
,而是分成了
Persistence
和
Fetcher2
個接口。
Persistence
處理持久相關如
save
和
remove
方法,
Fetcher
則處理
get
相關。這樣分的原因也很簡單,
persistence
是很穩固的,對象都可以共用一個接口如
save
(
Object
),而
fetcher
就千變萬化,需要分頁、排序等接口。
這樣設計是與業務層架構相關的。我采用的是
domain
對象(簡稱
DO
)
+
一層薄薄
fa?ade
的方式。
DO
處理自身的邏輯,包括持久功能。本身
DO
是沒有持久能力的,需要依靠注入的
persistence
接口,這里就體現按
Persistence
和
Fetcher
分開的一個好處,
persistence
所有
DO
可以共用一個,給編程帶來了方便。
Openfans
中采用的是
DO
繼承一個抽象
PersistentObject
類的方式,這樣
DO
方便的獲得了注入的能力和持久的能力。這樣做有何優缺點還需要做些討論,為了方便我也就先這么用。
PersistentObject
代碼如下:
public abstract class PersistentObject implements NeedPersist {
?????? private Persistence persistence;
?
?????? public void save() {
????????????? persistence.save(this);
?
?????? }
?
?????? public void remove() {
????????????? persistence.remove(this);
?????? }
?
?????? public void setPersistence(Persistence persistence) {
????????????? this.persistence = persistence;
?????? }
?
?????? public Persistence getPersistence() {
????????????? return persistence;
?????? }
}
這樣
DO
只需要注入
persistence
就獲得了持久的能力,而且可以把這種能力往下傳遞。
DO
獲得了持久能力,就有點接近富血模型的想法了,他能夠處理一些業務,做持久然后調用引用對象的業務和持久方法
(
從另外的角度看持久與業務其實是分不開的
)
。這樣把業務封裝在了領域本身,更適于用領域對象出發的方式去思考問題。領域層的
fa?ade
主要是為了
Transaction
管理和隱藏
DO
接口。這樣
DO
的業務方法都可以設置成
friendly
,僅相互間可見。
Fa?ade
就放在
domain
包中,它負責給
DO
注入
persistence bean
,調用
DO
的接口,提供給
controller
一個
use case
級別的接口,同時它也代理
fetcher
的接口。
有了這個架構,實現起來也不復雜,要配置的
bean
很少(現在還沒有使用
spring 2.0
將
DO
配置在容器中)。設計就從
DO
出發,明確它的屬性和方法,讓
hibernate
自己生成數據庫表。
??????
這樣設計也算是一次嘗試吧,其中肯定有很多考慮不周的地方,需要不斷的討論和改進。