Spring和AOP像一個(gè)強(qiáng)力的粘合劑,將完全獨(dú)立開(kāi)發(fā)的組件(或說(shuō)是模塊,下同)粘合成一個(gè)有機(jī)的,完整的,可擴(kuò)展的系統(tǒng)。正是有了這個(gè)粘合劑的幫助,才實(shí)現(xiàn)了比較徹底的獨(dú)立組件開(kāi)發(fā)。
說(shuō)它是“比較徹底”,是因?yàn)樗鼧O大的減少了組件之間的依賴。在你開(kāi)發(fā)一個(gè)組件時(shí),基本上不會(huì)因?yàn)槠渌M件沒(méi)有開(kāi)發(fā)完成,或出現(xiàn)Bug而影響到你的進(jìn)度。
但是,它并沒(méi)有完全消除開(kāi)發(fā)時(shí)組件之間的依賴,你仍然得依賴于其它組件提供的API接口。為此,我們不得不把一個(gè)組件拆成兩個(gè)jar包:一個(gè)component-api.jar,一個(gè)component-impl.jar。由于api包內(nèi)全是公用接口和Value Object,所以它相對(duì)穩(wěn)定,可以早早的提供出來(lái)。這樣,一個(gè)組件如果要使用另一個(gè)組件的服務(wù),在開(kāi)發(fā)階段,只須依賴于api包即可。運(yùn)行時(shí),Spring再根據(jù)服務(wù)提供組件的配置信息找到正確的實(shí)現(xiàn)類。
昨天,我們?cè)谝粋€(gè)討論會(huì)上發(fā)現(xiàn)了一個(gè)有趣的問(wèn)題:
組件UIA是一個(gè)UI組件,它要求提供一些數(shù)據(jù),于時(shí)它把自己的要求寫時(shí)接口ProviderA中。組件C1和C2是兩個(gè)不同的業(yè)務(wù)組件,它們的UI中都有使用UIA這個(gè)組件,而它們都提供了自己的數(shù)據(jù)接口ServiceC1和ServiceC2。
ProviderA所要求的方法,在ServiceC1和ServiceC2中都有提供。這個(gè)時(shí)候怎么做才能使各個(gè)組件完獨(dú)立呢。
一、讓ServiceC1和ServiceC2繼承于ProviderA。但是這樣將導(dǎo)致業(yè)務(wù)組件依賴于UI組件。有誰(shuí)知道一共有多少個(gè)UI組件需要依賴啊?而且UI組件是最易變的。
二、把ProviderA從uia.jar抽出來(lái),放到單獨(dú)的uia-api.jar中。這個(gè)就未免小題大做了。一個(gè)系統(tǒng)少說(shuō)也有幾十個(gè)UI組件,難道要生成上百個(gè)jar包不成?
三、把所有的UI的要求的API都抽出來(lái),放到一個(gè)ui-api.jar中。這樣jar包是少了,可是單個(gè)的UI組件就失去獨(dú)立性了。
上面三個(gè)方案,不管怎么管理UI組件的接口,都沒(méi)有解決業(yè)務(wù)組件依賴于不定數(shù)目的UI組件這個(gè)問(wèn)題。
最后,我們采用的方法是:把UI組件視為某個(gè)業(yè)務(wù)組件的子組件,UI組件自己不定義接口。所有對(duì)外的接口和對(duì)UI的接口,都放在業(yè)務(wù)組件的api包中。
這樣做,業(yè)務(wù)組件和UI組件都依賴于api包,互相之間沒(méi)有依賴。當(dāng)然,這樣做,UI組件就不能游離于大的業(yè)務(wù)組件之外。而我們采用這個(gè)方案的原因也在于,我們認(rèn)定為多個(gè)組件提供服務(wù)的UI組件是很少的。
顯然我們采用的方法只是就事論事的一個(gè)折衷方案。并沒(méi)有解決服務(wù)提供者和消費(fèi)者之間的交叉依賴。
要解決這種交叉依賴,我的思路是再提供一個(gè)接口之間的粘合機(jī)制。消費(fèi)者定義自己要求的服務(wù)接口,提供者定義自己提供的服務(wù)接口。最后用一個(gè)配置文件,將二者粘合起來(lái)。
目前,Spring還沒(méi)有提供這種功能。