使用AspectJ,Javassist和Java Proxy进行代码注入的实用介绍
這篇文章旨在為您提供您可能(或我寧愿說“將要”)需要的知識,并說服您學(xué)習(xí)代碼注入的基礎(chǔ)確實值得您花很少的時間。 我將介紹三種不同的現(xiàn)實情況,在這些情況下我需要進行代碼注入,并使用不同的工具解決每個問題,最適合手頭的約束。
為什么您需要它
關(guān)于AOP的優(yōu)勢(因此有代碼注入),已經(jīng)有很多論述 ,因此,從故障排除的角度來看,我將只專注于一些要點。
最酷的事情是,它使您能夠修改第三方封閉源類 ,甚至實際上是JVM類。 我們大多數(shù)人使用的是遺留代碼和我們沒有源代碼的代碼,因此不可避免地我們偶爾會遇到這些第三方二進制文件的局限性或錯誤,因此非常需要更改其中的一些小東西或深入了解代碼的行為。 如果沒有代碼注入,則無法修改代碼或添加對代碼增加可觀察性的支持。 同樣,您通常需要在生產(chǎn)環(huán)境中處理問題或收集信息,在生產(chǎn)環(huán)境中,您不能使用調(diào)試器和類似工具,而您通常至少可以以某種方式管理應(yīng)用程序的二進制文件和依賴項。 請考慮以下情況:
- 您正在將數(shù)據(jù)集合傳遞到閉源庫進行處理,并且?guī)熘械囊粋€方法對其中一個元素失敗,但是異常未提供有關(guān)它是哪個元素的信息。 您需要對其進行修改以記錄有問題的參數(shù)或?qū)⑵浒ㄔ诋惓V小?(并且您不能使用調(diào)試器,因為它僅在生產(chǎn)應(yīng)用程序服務(wù)器上發(fā)生。)
- 您需要收集應(yīng)用程序中重要方法的性能統(tǒng)計信息,包括在典型生產(chǎn)負載下的某些封閉源組件。 (在生產(chǎn)環(huán)境中,您當(dāng)然不能使用探查器,并且您希望產(chǎn)生最小的開銷。)
- 您使用JDBC批量發(fā)送大量數(shù)據(jù)到數(shù)據(jù)庫,而其中一個批量更新失敗。 您將需要一些不錯的方法來找出批次和包含的數(shù)據(jù)。
實際上,我已經(jīng)遇到了這三種情況(在其他情況下),稍后您將看到可能的實現(xiàn)。
閱讀本文時,您應(yīng)該牢記代碼注入的以下優(yōu)點:
- 代碼注入使您能夠修改您沒有源代碼的二進制類
- 注入的代碼可用于在無法使用傳統(tǒng)開發(fā)工具(例如探查器和調(diào)試器)的環(huán)境中收集各種運行時信息。
- 不要重復(fù)自己:當(dāng)您需要在多個地方使用相同的邏輯時,可以定義一次,然后將其注入所有這些地方。
- 使用代碼注入時,您無需修改??原始源文件,因此非常適合僅在有限時間內(nèi)進行的(可能是大規(guī)模的)更改,尤其是借助可以輕松打開和關(guān)閉代碼注入的工具(例如,具有加載時編織功能的AspectJ)。 典型的情況是性能指標收集和故障排除期間增加的日志記錄
- 您可以在構(gòu)建時靜態(tài)或靜態(tài)地注入代碼,或者在JVM加載目標類時動態(tài)注入代碼。
迷你詞匯
您可能會遇到以下與代碼注入和AOP有關(guān)的術(shù)語:
忠告
要注入的代碼。 通常,我們談?wù)摻ㄗh之前,之后和周圍,這些建議是在目標方法之前,之后或代替目標方法執(zhí)行的。 除了將代碼注入方法之外,還可以進行其他更改,例如,向類添加字段或接口。
AOP(面向方面??的編程)
一個編程范例聲稱,“跨領(lǐng)域關(guān)注點”(在許多地方都需要的邏輯,沒有一個單獨的類在哪里實現(xiàn))應(yīng)該實施一次,然后注入這些地方。 檢查維基百科以獲得更好的描述。
方面
AOP中的模塊化單位大致對應(yīng)于一個類–它可以包含不同的建議和切入點。
聯(lián)合點
程序中可能成為代碼注入目標的特定點,例如方法調(diào)用或方法條目。
切入點
粗略地說,切入點是一個表達式,它告訴代碼注入工具在哪里注入特定代碼段,即在哪個聯(lián)合點上應(yīng)用特定建議。 它只能選擇一個這樣的點(例如,單個方法的執(zhí)行),也可以選擇許多類似的點,例如,所有帶有自定義注釋(例如@MyBusinessMethod)的方法的執(zhí)行。
織造
將代碼(建議)注入目標位置(聯(lián)合點)的過程。
工具
有很多非常不同的工具可以完成這項工作,因此我們將首先了解它們之間的差異,然后我們將熟悉代碼注入工具的不同演化分支的三個杰出代表。
代碼注入工具的基本分類
一,抽象水平
表達要注入的邏輯以及表達應(yīng)在其中插入邏輯的切入點有多困難?
關(guān)于“建議”代碼:
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC,“ java / lang / System”,“ out”,“ Ljava / io / PrintStream;”);
由于級別太低,因此難以使用,但功能最強大。 通常,它們用于實現(xiàn)更高級別的工具,實際上很少需要使用它們。
關(guān)于將代碼注入到哪里的規(guī)范:
二。 當(dāng)魔術(shù)發(fā)生時
可以在不同的時間點注入代碼:
- 在運行時手動–您的代碼必須明確要求增強代碼,例如,通過手動實例化包裝目標對象的自定義代理(可以說這不是真正的代碼注入)
- 在加載時–在JVM加載目標類時執(zhí)行修改
- 在構(gòu)建時–在打包和部署應(yīng)用程序之前,您需要在構(gòu)建過程中添加額外的步驟來修改已編譯的類。
這些注射方式中的每一種都可能更適合于不同情況。
三, 它能做什么
代碼注入工具可以做什么或不能做什么都存在很大差異,其中一些可能性是:
- 在方法之前/之后/而不是方法中添加代碼–僅成員級方法還是靜態(tài)方法?
- 將字段添加到班級
- 添加新方法
- 制作一個類以實現(xiàn)接口
- 修改方法體內(nèi)的指令(例如方法調(diào)用)
- 修改泛型,注釋,訪問修飾符,更改常量值,…
- 刪除方法,字段等
選定的代碼注入工具
最著名的代碼注入工具是:
Java Proxy,Javassist和AspectJ實用介紹
我選擇了三種非常不同的成熟和流行的代碼注入工具,并將它們呈現(xiàn)在我親身經(jīng)歷的真實示例中。
無所不在的動態(tài)Java代理
Java.lang.reflect.Proxy使動態(tài)創(chuàng)建接口的代理成為可能,將所有調(diào)用轉(zhuǎn)發(fā)到目標對象。 它不是代碼注入工具,因為您不能在任何地方注入它,必須手動實例化并使用代理而不是原始對象,并且只能對接口執(zhí)行此操作,但是如我們所見,它仍然非常有用。
優(yōu)點:
- 它是JVM的一部分,因此隨處可見
- 您可以對不兼容的對象使用相同的代理(更確切地說是InvocationHandler) ,從而比平時更多地重用代碼
- 您可以節(jié)省精力,因為您可以輕松地將所有調(diào)用轉(zhuǎn)發(fā)到目標對象,而僅修改您感興趣的那些調(diào)用。 如果要手動實現(xiàn)代理,則需要實現(xiàn)相關(guān)接口的所有方法
缺點:
- 您只能為接口創(chuàng)建動態(tài)代理,如果代碼需要具體的類,則不能使用它
- 您必須實例化并手動應(yīng)用它,沒有神奇的自動注入功能
- 有點太冗長
- 它的功能非常有限,它只能在方法之前/之后/周圍執(zhí)行一些代碼
沒有代碼注入步驟-您必須手動應(yīng)用代理。
例
我正在使用JDBC PreparedStatement的批處理更新來修改數(shù)據(jù)庫中的許多數(shù)據(jù),并且由于違反完整性約束而導(dǎo)致其中一個批處理的處理失敗。 該異常沒有足夠的信息來找出導(dǎo)致失敗的數(shù)據(jù),因此我為PreparedStatement創(chuàng)建了一個動態(tài)代理,該代理記住了傳遞給每個批處理更新的值,并且在失敗的情況下會自動打印該批處理數(shù)字和數(shù)據(jù)。 有了這些信息,我就可以修復(fù)數(shù)據(jù),并保持解決方案就位,這樣,如果再次發(fā)生類似的問題,我將能夠找到原因并Swift解決。
該代碼的關(guān)鍵部分:
LoggingStatementDecorator.java –片段1
class LoggingStatementDecorator implements InvocationHandler {private PreparedStatement target;...private LoggingStatementDecorator(PreparedStatement target) { this.target = target; }@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {try {Object result = method.invoke(target, args);updateLog(method, args); // remember data, reset upon successful executionreturn result;} catch (InvocationTargetException e) {Throwable cause = e.getTargetException();tryLogFailure(cause);throw cause;}}private void tryLogFailure(Throwable cause) {if (cause instanceof BatchUpdateException) {int failedBatchNr = successfulBatchCounter + 1;Logger.getLogger("JavaProxy").warning("THE INJECTED CODE SAYS: " +"Batch update failed for batch# " + failedBatchNr +" (counting from 1) with values: [" +getValuesAsCsv() + "]. Cause: " + cause.getMessage());}} ...筆記:
要創(chuàng)建代理,您首先需要實現(xiàn)一個InvocationHandler及其調(diào)用方法,只要在代理上調(diào)用任何接口的方法,就會調(diào)用該方法
您可以通過java.lang.reflect。*對象訪問有關(guān)該調(diào)用的信息,例如,通過method.invoke將調(diào)用委派給代理對象
我們還有一個實用方法,用于為Prepared語句創(chuàng)建代理實例:
LoggingStatementDecorator.java –代碼段2
public static PreparedStatement createProxy(PreparedStatement target) {return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),new Class[] { PreparedStatement.class },new LoggingStatementDecorator(target)); };筆記:
- 您可以看到newProxyInstance調(diào)用使用一個類加載器,代理應(yīng)實現(xiàn)的接口數(shù)組以及應(yīng)將調(diào)用委派給其的調(diào)用處理程序(如果需要,處理程序本身必須管理對代理對象的引用)
然后按以下方式使用:
Main.java
... PreparedStatement rawPrepStmt = connection.prepareStatement("..."); PreparedStatement loggingPrepStmt = LoggingStatementDecorator.createProxy(rawPrepStmt); ... loggingPrepStmt.executeBatch(); ...筆記:
- 您會看到我們必須使用代理手動包裝原始對象,并在以后繼續(xù)使用代理
替代解決方案
可以通過不同的方式解決此問題,例如,通過創(chuàng)建一個實現(xiàn)PreparedStatement的非動態(tài)代理,并在記住批處理數(shù)據(jù)的同時將所有調(diào)用轉(zhuǎn)發(fā)到實際語句,但是對于具有許多方法的接口,這將是很多無聊的鍵入。 調(diào)用方還可以手動跟蹤已發(fā)送到準備好的語句的數(shù)據(jù),但這會因無關(guān)的關(guān)注而使邏輯模糊。
使用動態(tài)Java代理,我們可以得到非常干凈且易于實現(xiàn)的解決方案。
獨立的Javassist
JBoss Javassist是一個中間代碼注入工具,它提供了比字節(jié)碼操作庫更高級別的抽象,并且提供了有限的但仍然非常有用的操作功能。 要注入的代碼以字符串表示,您必須手動進入將其注入的類方法。 它的主要優(yōu)點是修改后的代碼對Javassist或其他任何東西都沒有新的運行時依賴項。 如果您在一家大公司工作,這可能是決定性因素,而在大公司中,由于法律和其他原因,很難部署其他開放源代碼庫(或幾乎任何其他庫),例如AspectJ。
優(yōu)點:
- Javassist修改的代碼不需要任何新的運行時依賴項,注入會在構(gòu)建時發(fā)生,并且注入的建議代碼本身不依賴于任何Javassist API
- 盡管比字節(jié)碼操作庫更高級,但注入的代碼是用Java語法編寫的,盡管包含在字符串中
- 可以完成您可能需要的大多數(shù)事情,例如“建議”方法調(diào)用和方法執(zhí)行
- 您可以同時實現(xiàn)構(gòu)建時注入(通過Java代碼或定制的Ant任務(wù)來執(zhí)行執(zhí)行/調(diào)用建議 )和加載時注入(通過實現(xiàn)自己的Java 5+代理 [thx to Anton])
缺點:
- 仍然有些太底層,因此難于使用–您必須處理一些方法的結(jié)構(gòu),并且注入的代碼未經(jīng)語法檢查
- Javassist沒有執(zhí)行注入的工具,因此您必須實現(xiàn)自己的注入代碼-包括不支持根據(jù)模式自動注入代碼
(有關(guān)沒有Javassist的大多數(shù)缺點的解決方案,請參見下面的GluonJ。)
使用Javassist,您可以創(chuàng)建一個類,該類使用Javassist API注入int目標代碼,并在編譯后將其作為構(gòu)建過程的一部分運行,例如,就像我曾經(jīng)通過自定義Ant任務(wù)所做的那樣。
例
我們需要在Java EE應(yīng)用程序中添加一些簡單的性能監(jiān)控,并且不允許我們部署任何未經(jīng)批準的開源庫(至少在沒有經(jīng)過耗時的審批過程的情況下)。 因此,我們使用Javassist將性能監(jiān)視代碼注入到我們的重要方法中,以及將重要的外部方法調(diào)用到的地方。
代碼注入器:
JavassistInstrumenter.java
public class JavassistInstrumenter {public void insertTimingIntoMethod(String targetClass, String targetMethod) throws NotFoundException, CannotCompileException, IOException {Logger logger = Logger.getLogger("Javassist");final String targetFolder = "./target/javassist";try {final ClassPool pool = ClassPool.getDefault();// Tell Javassist where to look for classes - into our ClassLoaderpool.appendClassPath(new LoaderClassPath(getClass().getClassLoader()));final CtClass compiledClass = pool.get(targetClass);final CtMethod method = compiledClass.getDeclaredMethod(targetMethod);// Add something to the beginning of the method:method.addLocalVariable("startMs", CtClass.longType);method.insertBefore("startMs = System.currentTimeMillis();");// And also to its very end:method.insertAfter("{final long endMs = System.currentTimeMillis();" +"iterate.jz2011.codeinjection.javassist.PerformanceMonitor.logPerformance(\"" +targetMethod + "\",(endMs-startMs));}");compiledClass.writeFile(targetFolder);// Enjoy the new $targetFolder/iterate/jz2011/codeinjection/javassist/TargetClass.classlogger.info(targetClass + "." + targetMethod +" has been modified and saved under " + targetFolder);} catch (NotFoundException e) {logger.warning("Failed to find the target class to modify, " +targetClass + ", verify that it ClassPool has been configured to look " +"into the right location");}}public static void main(String[] args) throws Exception {final String defaultTargetClass = "iterate.jz2011.codeinjection.javassist.TargetClass";final String defaultTargetMethod = "myMethod";final boolean targetProvided = args.length == 2;new JavassistInstrumenter().insertTimingIntoMethod(targetProvided? args[0] : defaultTargetClass, targetProvided? args[1] : defaultTargetMethod);} }筆記:
- 您可以看到“底層” –您必須顯式處理CtClass,CtMethod之類的對象,顯式添加局部變量等。
- Javassist在查找要修改的類方面非常靈活-它可以搜索類路徑,特定文件夾,JAR文件或包含JAR文件的文件夾
- 您將在編譯過程中編譯此類并運行其主要內(nèi)容
類固醇的Javassist:GluonJ
GluonJ是一個基于Javassist的AOP工具。 它可以使用自定義語法或Java 5注釋,并且圍繞“修訂器”的概念構(gòu)建。 Reviser是一個類(一個方面),它可以修改(即修改)特定的目標類并覆蓋其一個或多個方法(與繼承相反,修訂者的代碼實際上被強加于目標類內(nèi)部的原始代碼)。
優(yōu)點:
- 如果使用構(gòu)建時編織,則沒有運行時依賴性(加載時編織需要GluonJ代理庫或gluonj.jar)
- 使用GlutonJ的注釋的簡單Java語法-盡管自定義語法也很容易理解和易于使用
- 使用GlutonJ的JAR工具,Ant任務(wù)或在加載時動態(tài)輕松地自動織入目標類
- 支持構(gòu)建時和加載時編織
缺點:
- 一個方面只能修改一個類,而不能將同一段代碼注入多個類/方法
- 功率有限–僅在執(zhí)行任何代碼時或僅在特定上下文中執(zhí)行時(即從特定的類/方法中調(diào)用時),才提供字段/方法的添加和代碼的執(zhí)行,而不是在目標方法周圍/
如果您不需要將同一段代碼注入多個方法中,那么GluonJ比Javassist更加容易和更好地選擇,并且如果它的簡單性對您來說不是問題,那么它也比AspectJ更好的選擇。簡單。
全能方面
AspectJ是功能完善的AOP工具,它幾乎可以完成您可能想要的任何事情,包括修改靜態(tài)方法,添加新字段,在類的已實現(xiàn)接口列表中添加接口等。
AspectJ建議的語法有兩種,一種是Java語法的超集,具有諸如Aspect和Pointcut的其他關(guān)鍵字,另一種稱為@AspectJ –是標準Java 5,具有諸如@ Aspect,@ Pointcut,@ Around的批注。 后者也許更容易學(xué)習(xí)和使用,但功能卻不那么強大,因為它不像自定義AspectJ語法那樣具有表現(xiàn)力。
使用AspectJ,您可以定義要用非常有力的表達建議的聯(lián)合點,但是學(xué)習(xí)它們并使其正確起來可能并不困難。 對于AspectJ開發(fā),有一個有用的Eclipse插件– AspectJ開發(fā)工具 (AJDT)–但是上次嘗試時,它沒有我想要的那樣有用。
優(yōu)點:
- 功能強大,幾乎可以完成您可能需要的所有操作
- 強大的切入點表達式,用于定義在何處注入建議以及何時激活建議(包括一些運行時檢查)–完全啟用DRY,即寫入一次并多次注入
- 編譯時和加載時代碼注入(編織)
缺點:
- 修改后的代碼取決于AspectJ運行時庫
- 切入點表達式非常強大,但是可能很難正確使用它們,盡管AJDT插件可以部分可視化它們的效果,但對“調(diào)試”它們的支持不多
- 盡管基本用法非常簡單(可能會花一些時間才能開始使用(使用@ Aspect,@ Around和一個簡單的切入點表達式,如我們在示例中所見))
例
曾幾何時,我為具有相關(guān)性的封閉式LMS J2EE應(yīng)用程序編寫了一個插件,以致于無法在本地運行它。 在API調(diào)用期間,應(yīng)用程序內(nèi)部的某個方法失敗了,但該異常未包含足夠的信息來跟蹤問題的原因。 因此,我需要更改方法以在失敗時記錄其參數(shù)的值。
AspectJ代碼非常簡單:
LoggingAspect.java
@Aspect public class LoggingAspect {@Around("execution(private void TooQuiet3rdPartyClass.failingMethod(..))")public Object interceptAndLog(ProceedingJoinPoint invocation) throws Throwable {try {return invocation.proceed();} catch (Exception e) {Logger.getLogger("AspectJ").warning("THE INJECTED CODE SAYS: the method " +invocation.getSignature().getName() + " failed for the input '" +invocation.getArgs()[0] + "'. Original exception: " + e);throw e;}} }筆記:
- 方面是帶有@Aspect批注的普通Java類,它只是AspectJ的標記
- @Around注釋指示AspectJ執(zhí)行該方法,而不是與表達式匹配的方法,即代替TooQuiet3rdPartyClass的failingMethod。
- 周圍建議方法需要是公共的,返回一個對象,并采用一個特殊的AspectJ對象作為參數(shù),該對象攜帶有關(guān)調(diào)用的信息– ProceedingJoinPoint –并且可以具有任意名稱(實際上,這是簽名的最小形式,它可以更復(fù)雜。)
- 我們使用ProceedingJoinPoint將調(diào)用委派給原始目標(TooQuiet3rdPartyClass的實例),并在發(fā)生異常的情況下獲取參數(shù)的值
- 我使用了@Around建議,盡管@AfterThrowing會更簡單,更合適,但這可以更好地顯示AspectJ的功能,并且可以與上述動態(tài)Java代理示例進行很好的比較
由于我無法控制應(yīng)用程序的環(huán)境,因此無法啟用加載時編織,因此不得不在構(gòu)建時使用AspectJ的Ant任務(wù)來編織代碼,重新打包受影響的JAR并將其重新部署到服務(wù)器。
替代解決方案
好吧,如果您不能使用調(diào)試器,那么您的選擇就非常有限。 我唯一想到的替代解決方案是反編譯該類(非法!),將日志記錄添加到該方法中(前提是反編譯成功),重新編譯它,然后將原始.class替換為修改后的.class。
黑暗的一面
代碼注入和面向方面的編程非常強大,有時對于故障排除和作為應(yīng)用程序體系結(jié)構(gòu)的常規(guī)部分來說都是必不可少的,例如我們可以看到,例如在Java EE的Enterprise Java Beans中,諸如事務(wù)管理和安全性檢查等業(yè)務(wù)問題是注入到POJO中(盡管實現(xiàn)實際上更可能使用代理)或在Spring中。
但是,由于可能會降低可理解性,因此需要付出一定的代價,因為運行時行為和結(jié)構(gòu)與您根據(jù)源代碼所期望的不同(除非您知道還要檢查方面的源代碼,或者除非進行了注入)通過對目標類(例如Java EE的@Interceptors )的注釋進行顯式顯示。 因此,您必須仔細權(quán)衡代碼注入/ AOP的優(yōu)缺點-盡管合理使用它們不會比接口,工廠等掩蓋程序流。 關(guān)于掩蓋代碼的爭論可能經(jīng)常被高估了 。
如果您想看一下AOP的例子,請查看Glassbox的源代碼 ,它是JavaEE性能監(jiān)視工具(為此,您可能需要一張地圖 ,以免丟失太多)。
花式使用代碼注入和AOP
在故障排除過程中,代碼注入的主要應(yīng)用領(lǐng)域是日志記錄,通過提取并以某種方式傳達有關(guān)它的有趣運行時信息,可以更準確地了解應(yīng)用程序正在做什么。 但是,AOP除了簡單或復(fù)雜的日志記錄以外,還有許多有趣的用途,例如:
- 典型示例:Caching等人(例如: 在JBoss Cache中的AOP上 ),事務(wù)管理,日志記錄,安全性實施,持久性,線程安全,錯誤恢復(fù),方法的自動實現(xiàn)(例如toString,equals,hashCode),遠程處理
- 基于角色的編程 (例如OT / J ,使用BCEL)或數(shù)據(jù),上下文和交互體系結(jié)構(gòu)的實現(xiàn)
- 測試中
- 測試覆蓋率–注入代碼以記錄測試運行期間是否已執(zhí)行某行
- 突變測試 ( μJava , Jumble )–向應(yīng)用程序注入“隨機”突變并驗證測試是否失敗
- 模式測試 –通過AOP自動驗證代碼中正確實施了架構(gòu)/設(shè)計/最佳實踐建議
- 通過注入異常來模擬硬件/外部故障
- 幫助實現(xiàn)Java應(yīng)用程序的零周轉(zhuǎn)– JRebel對框架和服務(wù)器集成插件使用類似于AOP的方法 –即其插件 使用Javassist進行“二進制修補”
- 解決問題并避免使用AOP模式進行猴子編碼,例如Worker Object Creation(通過Runnable和ThreadPool / task隊列將直接調(diào)用轉(zhuǎn)變?yōu)楫惒秸{(diào)用)和Wormhole(使調(diào)用方的上下文信息對被調(diào)用方可用,而不必傳遞它們)遍歷所有層作為參數(shù),并且沒有ThreadLocal)–在《 AspectJ in Action》一書中進行了描述
- 處理遺留代碼–覆蓋對構(gòu)造函數(shù)的調(diào)用實例化的類(可以使用此類和類似的類來打破緊密耦合與可行的工作量), 確保向后兼容 o, 教導(dǎo)組件對環(huán)境變化做出正確反應(yīng)
- 保留API的向后兼容性,同時不阻止其演化能力,例如,在縮小/擴展返回類型( Bridge Method Injector –使用ASM)時添加向后兼容方法,或者通過重新添加舊方法并按照以下方式實現(xiàn)它們:新的API
- 將POJO轉(zhuǎn)換為JMX bean
摘要
我們已經(jīng)了解到,代碼注入對于故障排除是必不可少的,尤其是在處理封閉源代碼庫和復(fù)雜的部署環(huán)境時。 我們已經(jīng)看到了三種完全不同的代碼注入工具(動態(tài)Java代理,Javassist,AspectJ)應(yīng)用于實際問題,并討論了它們的優(yōu)缺點,因為不同的工具可能適用于不同的情況。 我們還提到了不應(yīng)過度使用代碼注入/ AOP,并查看了一些代碼注入/ AOP的高級應(yīng)用示例。
我希望您現(xiàn)在了解代碼注入如何為您提供幫助,并知道如何使用這三個工具。
源代碼
您可以從GitHub 獲取示例的完整文檔源代碼,不僅包括要注入的代碼,還包括目標代碼和易于構(gòu)建的支持。 最簡單的可能是:
git clone git://github.com/jakubholynet/JavaZone-Code-Injection.git cd JavaZone-Code-Injection/ cat README mvn -P javaproxy test mvn -P javassist test mvn -P aspectj test(Maven可能需要花費幾分鐘來下載其依賴項,插件和實際項目的依賴項。)
其他資源
- Spring對AOP的介紹
- dW: AOP @ Work:AOP的神話與現(xiàn)實
- AspectJ in Action的第1章 ,第2部分。 ed。
致謝
我要感謝所有為我提供這篇文章和演示文稿的人,包括我的大學(xué),JRebel同學(xué)和GluonJ的合著者。 千葉繁
參考: Holy Java博客上我們JCG合作伙伴 JakubHoly撰寫的有關(guān) 使用AspectJ,Javassist和Java Proxy進行代碼注入的實用介紹 。
相關(guān)文章:
- Spring和AspectJ的領(lǐng)域驅(qū)動設(shè)計
- 使用Spring AspectJ和Maven進行面向方面的編程
- 使用Spring AOP進行面向方面的編程
- 正確記錄應(yīng)用程序的10個技巧
翻譯自: https://www.javacodegeeks.com/2011/09/practical-introduction-into-code.html
總結(jié)
以上是生活随笔為你收集整理的使用AspectJ,Javassist和Java Proxy进行代码注入的实用介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 芜湖房产备案网上查询(芜湖房产备案网)
- 下一篇: 文件的权限linux(文件的权限 lin