資源描述:
《構(gòu)建javaagent,而不是使用框架-java開(kāi)發(fā)java經(jīng)驗(yàn)技巧》由會(huì)員上傳分享,免費(fèi)在線(xiàn)閱讀,更多相關(guān)內(nèi)容在工程資料-天天文庫(kù)。
1、構(gòu)建JavaAgent,而不是使用框架-編程開(kāi)發(fā)技術(shù)構(gòu)建JavaAgent,而不是使用框架木文由ImportNew?黃小非翻譯自javacodegeekso歡迎加入翻譯小組。轉(zhuǎn)載請(qǐng)見(jiàn)文末要求。Javaannotations自從被引入到Java之后,一直扮演著整合各種API的作用,尤其是對(duì)大型應(yīng)用框架而言。在這方面,Spring和Ilibcrneitc都是Javaannotation應(yīng)用的好例子僅僅需要增加幾行簡(jiǎn)單的Javaannotation代碼,就可以實(shí)現(xiàn)非常復(fù)雜的程序邏輯。盡管對(duì)這些API(的寫(xiě)法)存在一些爭(zhēng)論,但是大多數(shù)程序員認(rèn)為,只要使用得當(dāng),這種聲明式編程在形式上
2、還是很有表達(dá)能力的。不過(guò),只冇少量程序員基于Javaannotation編寫(xiě)框架API,或者應(yīng)用程序中間件。Z所以造成這種現(xiàn)象很主要的一個(gè)原因是,程序員們認(rèn)為匕陽(yáng)annotation會(huì)降低代碼的可讀性。在本文屮,我就想告訴大家,實(shí)現(xiàn)這些基于annotation的API其實(shí)并不是完全無(wú)用的,只要使用恰當(dāng)?shù)墓ぞ?,其?shí)你也并不需要了解太多Java內(nèi)部函數(shù)的知識(shí)。在實(shí)現(xiàn)基于annotation的API時(shí),很明顯的一個(gè)問(wèn)題就是:這些API在Java運(yùn)行時(shí)是不會(huì)被JVM處理的。這樣造成的結(jié)果就是,你沒(méi)法給一個(gè)用戶(hù)annotation賦予一個(gè)具體的含義。例如:如果我們定義了一個(gè)@Loga
3、nnotation,然后我們期望在標(biāo)注Y@Log的地方,每調(diào)用一次就形成一條日志記錄。classService{@LogvoiddoSomcthing(){//dosomething???}}單靠標(biāo)注本身寫(xiě)在哪里,是不口J能完成執(zhí)行程序邏輯的任務(wù)的,這就需要標(biāo)注的使用者去發(fā)起生成口志的任務(wù)。明顯,這種工作原理讓annotation看上去毫無(wú)意義,因?yàn)樵僬{(diào)用doSomething方法的時(shí)候,我們根本無(wú)法去觀察生成的log里面相應(yīng)的狀態(tài)。因此,annotation僅僅是作為一個(gè)標(biāo)記而存在,對(duì)程序邏輯來(lái)說(shuō)毫無(wú)貢獻(xiàn)可言。填坑為了克服上述的功能性局限,很多基于標(biāo)注的框架都采用了子類(lèi)覆
4、蓋類(lèi)方法的模式,來(lái)賦予特定標(biāo)注相關(guān)的程序邏輯功能。這種方法普遍使用了面向?qū)ο蟮募蓹C(jī)制。對(duì)于我們上面提到的@Log標(biāo)注來(lái)說(shuō),子類(lèi)實(shí)現(xiàn)機(jī)制會(huì)產(chǎn)生一個(gè)類(lèi)似于下面的類(lèi)LoggingService:classLoggingScrviccextendsService{?OverridevoiddoSomething(){Logger.logC^doSomething()wascalled");super.doSomethingO;當(dāng)然,上面定義這些類(lèi)的代碼通常是不需要程序員手寫(xiě)的,而是在Java運(yùn)行時(shí),通過(guò)諸如cglib或Javasssl這樣的庫(kù)來(lái)自動(dòng)生成。上面提到的兩個(gè)庫(kù)都提供了簡(jiǎn)
5、易的API,叮以用于生成增強(qiáng)型的子類(lèi)程序。這種把類(lèi)定義的過(guò)程放到運(yùn)行時(shí)的做法,其比較好的一個(gè)副作用是,在不特別規(guī)定程序規(guī)范,也不用修改已有的用戶(hù)代碼的前提下,能夠有效實(shí)現(xiàn)logging框架的功能。這樣就可以避免“顯式創(chuàng)建風(fēng)格”,也就不用新建一個(gè)Java源文件去手寫(xiě)代碼了。但是,可伸縮性好嗎?然而,上面的解決方案又帶來(lái)了另一個(gè)不足。我們通過(guò)自動(dòng)生成子類(lèi)的方式實(shí)現(xiàn)標(biāo)注的程序邏輯,必須保證在實(shí)例化的時(shí)候不能使用父類(lèi)的構(gòu)造函數(shù)。否則調(diào)用標(biāo)注方法的時(shí)候,述是無(wú)法完成調(diào)用添加H志的功能:原因很明顯,用父類(lèi)的構(gòu)造函數(shù)實(shí)例化對(duì)彖,無(wú)法創(chuàng)建出包含子類(lèi)覆蓋方法的正確實(shí)例(這是基本的面向?qū)ο蠖鄳B(tài)
6、的概念——譯者注)。更糟糕的是——當(dāng)使用上述方法進(jìn)行運(yùn)行吋代碼生成的時(shí)候LoggingService類(lèi)無(wú)法直接被實(shí)例化,因?yàn)镴ava編譯器在編譯的時(shí)候,運(yùn)行時(shí)期間生成的類(lèi)代碼還根本就不存在?;谏鲜鲈?,Spring或者Hibernate這些框架使用了“對(duì)象工廠”的模式。在其框架邏輯的范疇內(nèi),不允許直接(通過(guò)構(gòu)造函數(shù))對(duì)對(duì)彖進(jìn)行實(shí)例化,而是通過(guò)工廠類(lèi)來(lái)完成新建對(duì)象的工作。這種方式在Spring設(shè)計(jì)之初就被采納,用來(lái)管理各種beanoHibernates采用了相似的做法,大多數(shù)Iliberneites的實(shí)例被視為查詢(xún)的結(jié)果對(duì)彖,因此也不是顯式地來(lái)實(shí)例化的。然而,有一個(gè)特例是,
7、當(dāng)試圖存儲(chǔ)一個(gè)在數(shù)據(jù)庫(kù)屮還不存在的對(duì)象實(shí)例的時(shí)候,Hibernates的使用者需要用Hibernates返冋的對(duì)象來(lái)替換Z前存儲(chǔ)的對(duì)象實(shí)例。從這個(gè)例了來(lái)看Hibernates的問(wèn)題,忽略上述的替換會(huì)造成一個(gè)普通的初學(xué)者錯(cuò)誤。除此之外,幸虧有了這些工廠類(lèi),才能讓子類(lèi)化的方法對(duì)框架用戶(hù)透明,因?yàn)镴ava的類(lèi)型系統(tǒng)可以用子類(lèi)實(shí)例來(lái)替代其父類(lèi)。因此,只要是用戶(hù)需要調(diào)用自定義服務(wù)的地方,都可以用到LoggingService的實(shí)例。很遺憾,這種采用工廠類(lèi)來(lái)創(chuàng)建對(duì)彖的方法雖然(在理論上)被證明是可行的,但(在實(shí)際中)用來(lái)實(shí)現(xiàn)我