免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Java 編程的動(dòng)態(tài)性,第 6 部分: 利用 Javassist 進(jìn)行面向方面的更改
Dennis M. Sosnoski (dms@sosnoski.com), 總裁, Sosnoski Software Solutions, Inc.
2004 年 3 月 15 日
Java 顧問(wèn) Dennis Sosnoski 在他的關(guān)于 Javassist 框架的三期文章中將精華部分留在了最后。這次他展現(xiàn)了 Javassist 對(duì)搜索-替換的支持是如何使對(duì) Java 字節(jié)碼的編輯變得像文本編輯器的“替換所有(Replace All )”命令一樣容易的。想報(bào)告所有寫(xiě)入特定字段的內(nèi)容或者對(duì)方法調(diào)用中參數(shù)的更改中的補(bǔ)丁嗎?Javassist 使這變得很容易,Dennis 向您展示了其做法。
本系列的第 4 部分第 5 部分討論了如何用 Javassist 對(duì)二進(jìn)制類(lèi)進(jìn)行局部更改。這次您將學(xué)習(xí)以一種更強(qiáng)大的方式使用該框架,從而充分利用 Javassist 對(duì)在字節(jié)碼中查找所有特定方法或者字段的支持。對(duì)于 Javassist 功能而言,這個(gè)功能至少與它以類(lèi)似源代碼的方式指定字節(jié)碼的能力同樣重要。對(duì)選擇替換操作的支持也有助于使 Javasssist 成為一個(gè)在標(biāo)準(zhǔn) Java 代碼中增加面向方面的編程功能的絕好工具。
第 5 部分介紹了 Javassist 是如何讓您攔截類(lèi)加載過(guò)程的 ―― 甚至在二進(jìn)制類(lèi)表示正在被加載的時(shí)候?qū)λ鼈冞M(jìn)行更改。這篇文章中討論的系統(tǒng)字節(jié)碼轉(zhuǎn)換可以用于靜態(tài)類(lèi)文件轉(zhuǎn)換,也可以用于運(yùn)行時(shí)攔截,但是在運(yùn)行時(shí)使用尤其有用。
Javassist 提供了兩種不同的系統(tǒng)字節(jié)碼修改的處理方法。第一種技術(shù)是使用 javassist.CodeConverter 類(lèi),使用起來(lái)要稍微簡(jiǎn)單一些,但是可以完成的任務(wù)有很多限制。第二種技術(shù)使用 javassist.ExprEditor 類(lèi)的自定義子類(lèi),它稍微復(fù)雜一些,但是所增加的靈活性足以抵銷(xiāo)所付出的努力。在本文中我將分析這兩種方法的例子。
系統(tǒng)字節(jié)碼修改的第一種 Javassist 技術(shù)使用 javassist.CodeConverter 類(lèi)。要利用這種技術(shù),只需要?jiǎng)?chuàng)建 CodeConverter 類(lèi)的一個(gè)實(shí)例并用一個(gè)或者多個(gè)轉(zhuǎn)換操作配置它。每一個(gè)轉(zhuǎn)換都是用識(shí)別轉(zhuǎn)換類(lèi)型的方法調(diào)用來(lái)配置的。轉(zhuǎn)換類(lèi)型可分為三類(lèi):方法調(diào)用轉(zhuǎn)換、字段訪問(wèn)轉(zhuǎn)換和新對(duì)象轉(zhuǎn)換。
第 1 部分, “類(lèi)和類(lèi)裝入”(2003 年 4 月)
第 2 部分, “引入反射”(2003 年 6 月)
第 3 部分, “應(yīng)用反射”(2003 年 7 月)
第 4 部分, “用 Javassist 進(jìn)行類(lèi)轉(zhuǎn)換”(2003 年 9 月)
第 5 部分, “動(dòng)態(tài)轉(zhuǎn)換類(lèi)”(2004 年 2 月)
清單 1 給出了使用方法調(diào)用轉(zhuǎn)換的一個(gè)例子。在這個(gè)例子中,轉(zhuǎn)換只是增加了一個(gè)方法正在被調(diào)用的通知。在代碼中,首先得到將要使用的 javassist.ClassPool 實(shí)例,將它配置為與一個(gè)翻譯器一同工作 (正如在前面第 5 部分 所看到的)。然后,通過(guò) ClassPool 訪問(wèn)兩個(gè)方法定義。第一個(gè)方法定義針對(duì)的是要監(jiān)視的“set”類(lèi)型的方法(類(lèi)和方法名來(lái)自命令行參數(shù)),第二個(gè)方法定義針對(duì)的是 reportSet() 方法 ,它位于 TranslateConvert 類(lèi)中,并會(huì)報(bào)告對(duì)第一個(gè)方法的調(diào)用。
有了方法信息后,就可以用 CodeConverter insertBeforeMethod() 配置一個(gè)轉(zhuǎn)換,以在每次調(diào)用這個(gè) set 方法之前增加一個(gè)對(duì)報(bào)告方法的調(diào)用。然后所要做的就是將這個(gè)轉(zhuǎn)換器應(yīng)用到一個(gè)或者多個(gè)類(lèi)上。在清單 1 的代碼中,我是通過(guò)調(diào)用類(lèi)對(duì)象的 instrument() 方法,在 ConverterTranslator 內(nèi)部類(lèi)的 onWrite() 方法中完成這項(xiàng)工作的。這將自動(dòng)對(duì)從 ClassPool 實(shí)例中加載的每一個(gè)類(lèi)應(yīng)用這個(gè)轉(zhuǎn)換。
public class TranslateConvert { public static void main(String[] args) { if (args.length >= 3) { try { // set up class loader with translator ConverterTranslator xlat = new ConverterTranslator(); ClassPool pool = ClassPool.getDefault(xlat); CodeConverter convert = new CodeConverter(); CtMethod smeth = pool.get(args[0]). getDeclaredMethod(args[1]); CtMethod pmeth = pool.get("TranslateConvert"). getDeclaredMethod("reportSet"); convert.insertBeforeMethod(smeth, pmeth); xlat.setConverter(convert); Loader loader = new Loader(pool); // invoke "main" method of application class String[] pargs = new String[args.length-3]; System.arraycopy(args, 3, pargs, 0, pargs.length); loader.run(args[2], pargs); } catch ... } } else { System.out.println("Usage: TranslateConvert " + "clas-name set-name main-class args..."); } } public static void reportSet(Bean target, String value) { System.out.println("Call to set value " + value); } public static class ConverterTranslator implements Translator { private CodeConverter m_converter; private void setConverter(CodeConverter convert) { m_converter = convert; } public void start(ClassPool pool) {} public void onWrite(ClassPool pool, String cname) throws NotFoundException, CannotCompileException { CtClass clas = pool.get(cname); clas.instrument(m_converter); } } }
配置轉(zhuǎn)換是一個(gè)相當(dāng)復(fù)雜的操作,但是設(shè)置好以后,在它工作時(shí)就不用費(fèi)什么心了。清單 2 給出了代碼示例,可以作為測(cè)試案例。這里 Bean 提供了具有類(lèi)似 bean 的 get 和 set 方法的測(cè)試對(duì)象, BeanTest 程序用這些方法來(lái)訪問(wèn)值。
public class Bean { private String m_a; private String m_b; public Bean() {} public Bean(String a, String b) { m_a = a; m_b = b; } public String getA() { return m_a; } public String getB() { return m_b; } public void setA(String string) { m_a = string; } public void setB(String string) { m_b = string; } } public class BeanTest { private Bean m_bean; private BeanTest() { m_bean = new Bean("originalA", "originalB"); } private void print() { System.out.println("Bean values are " + m_bean.getA() + " and " + m_bean.getB()); } private void changeValues(String lead) { m_bean.setA(lead + "A"); m_bean.setB(lead + "B"); } public static void main(String[] args) { BeanTest inst = new BeanTest(); inst.print(); inst.changeValues("new"); inst.print(); } }
如果直接運(yùn)行清單 2 中的 中的 BeanTest 程序,則輸出如下:
[dennis]$ java -cp . BeanTest Bean values are originalA and originalB Bean values are newA and newB
如果用清單 1 中的 TranslateConvert 程序運(yùn)行它并指定監(jiān)視其中的一個(gè) set 方法,那么輸出將如下所示:
[dennis]$ java -cp .:javassist.jar TranslateConvert Bean setA BeanTest Bean values are originalA and originalB Call to set value newA Bean values are newA and newB
每項(xiàng)工作都與以前一樣,但是現(xiàn)在在執(zhí)行這個(gè)程序時(shí),所選的方法被調(diào)用時(shí)會(huì)有一個(gè)通知。
在這個(gè)例子中,可以用其他的方法容易地實(shí)現(xiàn)同樣的效果,例如通過(guò)使用第 4 部分 中的技術(shù)在實(shí)際的 set 方法體中增加代碼。這里的區(qū)別是,在使用位置增加代碼讓我有了靈活性。例如,可以容易地修改 TranslateConvert.ConverterTranslator onWrite() 方法來(lái)檢查正在加載的類(lèi)名,并只轉(zhuǎn)換在我想要監(jiān)視的類(lèi)的清單中列出的類(lèi)。直接在 set 方法體中添加代碼無(wú)法進(jìn)行這種有選擇的監(jiān)視。
系統(tǒng)字節(jié)碼轉(zhuǎn)換由于提供了靈活性而使其成為為標(biāo)準(zhǔn) Java 代碼實(shí)現(xiàn)面向方面的擴(kuò)展的強(qiáng)大工具。在本文后面您會(huì)看到更多這方面的內(nèi)容。
由 CodeConverter 處理的轉(zhuǎn)換很有用,但是有局限性。例如,如果希望在調(diào)用目標(biāo)方法之前或者之后調(diào)用一個(gè)監(jiān)視方法,那么這個(gè)監(jiān)視方法必須定義為 static void 并且必須先接受一個(gè)目標(biāo)方法的類(lèi)的參數(shù),然后是與目標(biāo)方法所要求的同樣數(shù)量和類(lèi)型的參數(shù)。
這種嚴(yán)格的結(jié)構(gòu)意味著監(jiān)視方法需要與目標(biāo)類(lèi)和方法完全匹配。舉一個(gè)例子,假設(shè)我改變了清單 1 中 reportSet() 方法的定義,讓它接受一個(gè)一般性的 java.lang.Object 參數(shù),想使它可以用于不同的目標(biāo)類(lèi):
public static void reportSet(Object target, String value) { System.out.println("Call to set value " + value); }
編譯沒(méi)有問(wèn)題,但是當(dāng)我運(yùn)行它時(shí)它就會(huì)中斷:
[dennis]$ java -cp .:javassist.jar TranslateConvert Bean setA BeanTest Bean values are A and B java.lang.NoSuchMethodError: TranslateConvert.reportSet(LBean;Ljava/lang/String;)V at BeanTest.changeValues(BeanTest.java:17) at BeanTest.main(BeanTest.java:23) at ...
有辦法繞過(guò)這種限制。一種解決方案是在運(yùn)行時(shí)實(shí)際生成與目標(biāo)方法相匹配的自定義監(jiān)視方法。不過(guò)這要做很多工作,在本文中我不打算試驗(yàn)這種方法。幸運(yùn)的是,Javassist 還提供了另一種處理系統(tǒng)字節(jié)碼轉(zhuǎn)換的方法。這種方法使用 javassist.ExprEditor ,與 CodeConverter 相比,它更靈活、也更強(qiáng)大。
用 CodeConverter 進(jìn)行字節(jié)碼轉(zhuǎn)換與用 javassist.ExprEditor 的原理一樣。不過(guò), ExprEditor 方式也許更難理解一些,所以我首先展示基本原理,然后再加入實(shí)際的轉(zhuǎn)換。
清單 3 顯示了如何用 ExprEditor 來(lái)報(bào)告面向方面的轉(zhuǎn)換的可能目標(biāo)的基本項(xiàng)目。這里我在自己的 VerboseEditor 中派生了 ExprEditor 子類(lèi),重寫(xiě)了三個(gè)基本的類(lèi)方法 ―― 它們的名字都是 edit() ,但是有不同的參數(shù)類(lèi)型。如清單 1 中的代碼,我實(shí)際上是在 DissectionTranslator 內(nèi)部類(lèi)的 onWrite() 方法中使用這個(gè)子類(lèi),對(duì)從 ClassPool 實(shí)例中加載的每一個(gè)類(lèi),在對(duì)類(lèi)對(duì)象的 instrument() 方法的調(diào)用中傳遞一個(gè)實(shí)例。
public class Dissect { public static void main(String[] args) { if (args.length >= 1) { try { // set up class loader with translator Translator xlat = new DissectionTranslator(); ClassPool pool = ClassPool.getDefault(xlat); Loader loader = new Loader(pool); // invoke the "main" method of the application class String[] pargs = new String[args.length-1]; System.arraycopy(args, 1, pargs, 0, pargs.length); loader.run(args[0], pargs); } catch (Throwable ex) { ex.printStackTrace(); } } else { System.out.println ("Usage: Dissect main-class args..."); } } public static class DissectionTranslator implements Translator { public void start(ClassPool pool) {} public void onWrite(ClassPool pool, String cname) throws NotFoundException, CannotCompileException { System.out.println("Dissecting class " + cname); CtClass clas = pool.get(cname); clas.instrument(new VerboseEditor()); } } public static class VerboseEditor extends ExprEditor { private String from(Expr expr) { CtBehavior source = expr.where(); return " in " + source.getName() + "(" + expr.getFileName() + ":" + expr.getLineNumber() + ")"; } public void edit(FieldAccess arg) { String dir = arg.isReader() ? "read" : "write"; System.out.println(" " + dir + " of " + arg.getClassName() + "." + arg.getFieldName() + from(arg)); } public void edit(MethodCall arg) { System.out.println(" call to " + arg.getClassName() + "." + arg.getMethodName() + from(arg)); } public void edit(NewExpr arg) { System.out.println(" new " + arg.getClassName() + from(arg)); } } }
清單 4 顯示了對(duì)清單 2 中的 BeanTest 程序運(yùn)行清單 3 中的 Dissect 程序所產(chǎn)生的輸出。它給出了加載的每一個(gè)類(lèi)的每一個(gè)方法中所做的工作的詳細(xì)分析,列出了所有方法調(diào)用、字段訪問(wèn)和新對(duì)象創(chuàng)建。
[dennis]$ java -cp .:javassist.jar Dissect BeanTest Dissecting class BeanTest new Bean in BeanTest(BeanTest.java:7) write of BeanTest.m_bean in BeanTest(BeanTest.java:7) read of java.lang.System.out in print(BeanTest.java:11) new java.lang.StringBuffer in print(BeanTest.java:11) call to java.lang.StringBuffer.append in print(BeanTest.java:11) read of BeanTest.m_bean in print(BeanTest.java:11) call to Bean.getA in print(BeanTest.java:11) call to java.lang.StringBuffer.append in print(BeanTest.java:11) call to java.lang.StringBuffer.append in print(BeanTest.java:11) read of BeanTest.m_bean in print(BeanTest.java:11) call to Bean.getB in print(BeanTest.java:11) call to java.lang.StringBuffer.append in print(BeanTest.java:11) call to java.lang.StringBuffer.toString in print(BeanTest.java:11) call to java.io.PrintStream.println in print(BeanTest.java:11) read of BeanTest.m_bean in changeValues(BeanTest.java:16) new java.lang.StringBuffer in changeValues(BeanTest.java:16) call to java.lang.StringBuffer.append in changeValues(BeanTest.java:16) call to java.lang.StringBuffer.append in changeValues(BeanTest.java:16) call to java.lang.StringBuffer.toString in changeValues(BeanTest.java:16) call to Bean.setA in changeValues(BeanTest.java:16) read of BeanTest.m_bean in changeValues(BeanTest.java:17) new java.lang.StringBuffer in changeValues(BeanTest.java:17) call to java.lang.StringBuffer.append in changeValues(BeanTest.java:17) call to java.lang.StringBuffer.append in changeValues(BeanTest.java:17) call to java.lang.StringBuffer.toString in changeValues(BeanTest.java:17) call to Bean.setB in changeValues(BeanTest.java:17) new BeanTest in main(BeanTest.java:21) call to BeanTest.print in main(BeanTest.java:22) call to BeanTest.changeValues in main(BeanTest.java:23) call to BeanTest.print in main(BeanTest.java:24) Dissecting class Bean write of Bean.m_a in Bean(Bean.java:10) write of Bean.m_b in Bean(Bean.java:11) read of Bean.m_a in getA(Bean.java:15) read of Bean.m_b in getB(Bean.java:19) write of Bean.m_a in setA(Bean.java:23) write of Bean.m_b in setB(Bean.java:27) Bean values are originalA and originalB Bean values are newA and newB
通過(guò)在 VerboseEditor 中實(shí)現(xiàn)適當(dāng)?shù)姆椒?,可以容易地增加?duì)報(bào)告強(qiáng)制類(lèi)型轉(zhuǎn)換、 instanceof 檢查和 catch 塊的支持。但是只列出有關(guān)這些組件項(xiàng)的信息有些乏味,所以讓我們來(lái)實(shí)際修改項(xiàng)目吧。
清單 4對(duì)類(lèi)的剖析列出了基本組件操作。容易看出在實(shí)現(xiàn)面向方面的功能時(shí)使用這些操作會(huì)多么有用。例如,報(bào)告對(duì)所選字段的所有寫(xiě)訪問(wèn)的記錄器(logger)在許多應(yīng)用程序中都會(huì)發(fā)揮作用。無(wú)論如何,我已經(jīng)承諾要為您介紹如何完成 這類(lèi)工作。
幸運(yùn)的是,就本文討論的主題來(lái)說(shuō), ExprEditor 不但讓我知道代碼中有什么操作,它還讓我可以修改所報(bào)告的操作。在不同的 ExprEditor.edit() 方法調(diào)用中傳遞的參數(shù)類(lèi)型分別定義一種 replace() 方法。如果向這個(gè)方法傳遞一個(gè)普通 Javassist 源代碼格式的語(yǔ)句(在第 4 部分中介紹),那么這個(gè)語(yǔ)句將編譯為字節(jié)碼,并且用來(lái)替換原來(lái)的操作。這使對(duì)字節(jié)碼的切片和切塊變得容易。
清單 5 顯示了一個(gè)代碼替換的應(yīng)用程序。在這里我不是記錄操作,而是選擇實(shí)際修改存儲(chǔ)在所選字段中的 String 值。在 FieldSetEditor 中,我實(shí)現(xiàn)了匹配字段訪問(wèn)的方法簽名。在這個(gè)方法中,我只檢查兩樣?xùn)|西:字段名是否是我所查找的,操作是否是一個(gè)存儲(chǔ)過(guò)程。找到匹配后,就用使用實(shí)際的 TranslateEditor 應(yīng)用程序類(lèi)中 reverse() 方法調(diào)用的結(jié)果來(lái)替換原來(lái)的存儲(chǔ)。 reverse() 方法就是將原來(lái)字符串中的字母順序顛倒并輸出一條消息表明它已經(jīng)使用過(guò)了。
public class TranslateEditor { public static void main(String[] args) { if (args.length >= 3) { try { // set up class loader with translator EditorTranslator xlat = new EditorTranslator(args[0], new FieldSetEditor(args[1])); ClassPool pool = ClassPool.getDefault(xlat); Loader loader = new Loader(pool); // invoke the "main" method of the application class String[] pargs = new String[args.length-3]; System.arraycopy(args, 3, pargs, 0, pargs.length); loader.run(args[2], pargs); } catch (Throwable ex) { ex.printStackTrace(); } } else { System.out.println("Usage: TranslateEditor clas-name " + "field-name main-class args..."); } } public static String reverse(String value) { int length = value.length(); StringBuffer buff = new StringBuffer(length); for (int i = length-1; i >= 0; i--) { buff.append(value.charAt(i)); } System.out.println("TranslateEditor.reverse returning " + buff); return buff.toString(); } public static class EditorTranslator implements Translator { private String m_className; private ExprEditor m_editor; private EditorTranslator(String cname, ExprEditor editor) { m_className = cname; m_editor = editor; } public void start(ClassPool pool) {} public void onWrite(ClassPool pool, String cname) throws NotFoundException, CannotCompileException { if (cname.equals(m_className)) { CtClass clas = pool.get(cname); clas.instrument(m_editor); } } } public static class FieldSetEditor extends ExprEditor { private String m_fieldName; private FieldSetEditor(String fname) { m_fieldName = fname; } public void edit(FieldAccess arg) throws CannotCompileException { if (arg.getFieldName().equals(m_fieldName) && arg.isWriter()) { StringBuffer code = new StringBuffer(); code.append("$0."); code.append(arg.getFieldName()); code.append("=TranslateEditor.reverse($1);"); arg.replace(code.toString()); } } } }
如果對(duì)清單 2 中的 BeanTest 程序運(yùn)行清單 5 中的 TranslateEditor 程序,結(jié)果如下:
[dennis]$ java -cp .:javassist.jar TranslateEditor Bean m_a BeanTest TranslateEditor.reverse returning Alanigiro Bean values are Alanigiro and originalB TranslateEditor.reverse returning Awen Bean values are Awen and newB
我成功地在每一次存儲(chǔ)到 Bean.m_a 字段時(shí),加入了一個(gè)對(duì)添加的代碼的調(diào)用(一次是在構(gòu)造函數(shù)中,一次是在 set 方法中)。我可以通過(guò)對(duì)從字段的加載實(shí)現(xiàn)類(lèi)似的修改而得到反向的效果,不過(guò)我個(gè)人認(rèn)為顛倒值比開(kāi)始使用的值有意思得多,所以我選擇使用它們。
本文介紹了用 Javassist 可以容易地完成系統(tǒng)字節(jié)碼轉(zhuǎn)換。將本文與上兩期文章結(jié)合在一起,您應(yīng)該有了在 Java 應(yīng)用程序中實(shí)現(xiàn)自己面向方面的轉(zhuǎn)換的堅(jiān)實(shí)基礎(chǔ),這個(gè)轉(zhuǎn)換過(guò)程可以作為單獨(dú)的編譯步驟,也可以在運(yùn)行時(shí)完成。
要想對(duì)這種方法的強(qiáng)大之處有更好的了解,還可以分析用 Javassis 建立的 JBoss Aspect Oriented Programming Project (JBossAOP)。JBossAOP 使用一個(gè) XML 配置文件來(lái)定義在應(yīng)用程序類(lèi)中完成的所有不同的操作。其中包括對(duì)字段訪問(wèn)或者方法調(diào)用使用攔截器,在現(xiàn)有類(lèi)中添加 mix-in 接口實(shí)現(xiàn)等。JBossAOP 將被加入正在開(kāi)發(fā)的 JBoss 應(yīng)用程序服務(wù)器版本中,但是也可以在 JBoss 以外作為單獨(dú)的工具提供給應(yīng)用程序使用。
本系列的下一步將介紹 Byte Code Engineering Library (BCEL),這是 Apache Software Foundation 的 Jakarta 項(xiàng)目的一部分。BCEL 是 Java classworking 最廣泛使用的一種框架。它使用與我們?cè)谧罱@三篇文章中看到的 Javassist 方法的不同方法處理字節(jié)碼,注重個(gè)別的字節(jié)碼指令而不是 Javassist 所強(qiáng)調(diào)的源代碼級(jí)別的工作。下個(gè)月將分析在字節(jié)碼匯編器(assembler)級(jí)別工作的全部細(xì)節(jié)。
您可以參閱本文在 developerWorks 全球站點(diǎn)上的英文原文.
參閱 Dennis Sosnoski 的Java 編程的動(dòng)態(tài)性 系列的其他部分。
下載本文的示例代碼。
Javassist 是東京技術(shù)學(xué)院數(shù)學(xué)和計(jì)算機(jī)科學(xué)系的 Shigeru Chiba 開(kāi)發(fā)的。它最近加入了開(kāi)放源代碼JBoss 應(yīng)用服務(wù)器項(xiàng)目,并成為其中新增加的面向方面的編程功能的基礎(chǔ)。從 Sourceforge 上的JBoss 項(xiàng)目文件頁(yè)面下載 Javassist 的最新版本。
想看用 Javassist 構(gòu)建的面向方面的編程框架嗎?請(qǐng)看JBossAOP 項(xiàng)目。
從 Peter Haggar 的“Java bytecode: Understanding bytecode makes you a better programmer”( developerWorks,2001 年 7 月)中學(xué)習(xí)更多關(guān)于 Java 字節(jié)碼設(shè)計(jì)的內(nèi)容。
希望找到有關(guān)面向方面編程的更多內(nèi)容嗎?請(qǐng)參閱 Nicholas Lesiechi 的“Improve modularity with aspect-oriented programming”( developerWorks,2002 年 1 月),以了解有關(guān)使用 Aspectj 語(yǔ)言的簡(jiǎn)要介紹。Andrew Glover 新發(fā)表的文章“AOP banishes the tight-coupling blues”( developerWorks,2004 年 2 月)展示了 AOP 的一個(gè)功能設(shè)計(jì)概念 ―― 靜態(tài)橫切 ―― 是如何將纏繞在一起的、緊密耦合的代碼轉(zhuǎn)變?yōu)閺?qiáng)大的、可擴(kuò)展的企業(yè)應(yīng)用程序。
開(kāi)放源代碼Jikes 項(xiàng)目為 Java 編程語(yǔ)言提供了一個(gè)非??焖俸透叨燃嫒莸木幾g器。用它以老的方式(即從 Java 源代碼)生成字節(jié)碼。
訪問(wèn)Developer Bookstore,獲得技術(shù)圖書(shū)的完整列表,其中包括數(shù)百本 Java 相關(guān)的圖書(shū)。
developerWorks Java 技術(shù)專(zhuān)區(qū) 上有數(shù)百篇關(guān)于 Java 技術(shù)各個(gè)方面的文章。
Dennis Sosnoski(dms@sosnoski.com)是西雅圖地區(qū)的 Java 咨詢公司Sosnoski Software Solutions, Inc.的創(chuàng)始人和首席顧問(wèn)。他有 30 多年的專(zhuān)業(yè)軟件開(kāi)發(fā)經(jīng)驗(yàn),最近幾年致力于服務(wù)器端的 Java 技術(shù),包括 servlet、Enterprise JavaBeans 和 XML。他經(jīng)常在全國(guó)性的會(huì)議上就 Java 中的 XML 和 J2EE 技術(shù)發(fā)表言論。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
《Java程序設(shè)計(jì)教程》07 字符串和常用庫(kù)類(lèi)
Java關(guān)于JSON數(shù)據(jù)格式轉(zhuǎn)換
J2EE程序的性能優(yōu)化技巧
spring依賴注入
SpringMVC 使用JSR
簡(jiǎn)單講講@SpringBootApplication
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服