日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java代理模式_Java代理

發(fā)布時(shí)間:2023/12/3 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java代理模式_Java代理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

java代理模式

本文是我們名為“ 高級(jí)Java ”的學(xué)院課程的一部分。

本課程旨在幫助您最有效地使用Java。 它討論了高級(jí)主題,包括對(duì)象創(chuàng)建,并發(fā),序列化,反射等。 它將指導(dǎo)您完成Java掌握的旅程! 在這里查看 !

目錄

1.簡(jiǎn)介 2. Java代理基礎(chǔ) 3. Java代理和規(guī)范 4.編寫您的第一個(gè)Java代理 5.運(yùn)行Java代理 6.接下來 7.下載源代碼

1.簡(jiǎn)介

在本教程的最后一部分中,我們將討論Java代理,這對(duì)于在那里的常規(guī)Java開發(fā)人員是一個(gè)真正的魔咒。 通過執(zhí)行字節(jié)碼的直接修改,Java代理能夠“侵入”運(yùn)行時(shí)在JVM上運(yùn)行的Java應(yīng)用程序的執(zhí)行。 Java代理的功能和危險(xiǎn)一樣強(qiáng)大:它們幾乎可以執(zhí)行所有操作,但是如果出現(xiàn)問題,它們很容易使JVM崩潰。

這部分的目的是通過解釋Java代理如何工作,如何運(yùn)行它們以及展示一些簡(jiǎn)單的示例來揭開Java代理的神秘面紗,Java代理顯然具有優(yōu)勢(shì)。

2. Java代理基礎(chǔ)

本質(zhì)上,Java代理是遵循一組嚴(yán)格約定的常規(guī)Java類。 代理類必須實(shí)現(xiàn)一個(gè)public static void premain(String agentArgs, Instrumentation inst)方法,該方法成為代理的入口點(diǎn)(類似于常規(guī)Java應(yīng)用程序的main方法)。

初始化Java虛擬機(jī)(JVM)后,將按照在JVM啟動(dòng)時(shí)指定代理的順序調(diào)用每個(gè)代理的每個(gè)此類premain(String agentArgs, Instrumentation inst)方法。 完成此初始化步驟后,將調(diào)用真實(shí)的Java應(yīng)用程序main方法。

但是,如果該類未實(shí)現(xiàn)public static void premain(String agentArgs, Instrumentation inst)方法,則JVM將嘗試查找并調(diào)用另一個(gè)重載版本的public static void premain(String agentArgs) 。 請(qǐng)注意,每個(gè)premain方法必須返回才能啟動(dòng)階段。

最后但并非最不重要的一點(diǎn)是,Java代理類還可以具有在JVM啟動(dòng)后啟動(dòng)代理時(shí)使用的public static void agentmain(String agentArgs, Instrumentation inst)或public static void agentmain(String agentArgs)方法。

乍看之下看起來很簡(jiǎn)單,但Java代理實(shí)現(xiàn)還應(yīng)提供其他一些內(nèi)容作為其包裝的一部分:清單。 清單文件通常位于META-INF文件夾中,名為MANIFEST.MF ,包含與包分發(fā)有關(guān)的各種元數(shù)據(jù)。

我們?cè)诒窘坛讨胁⑽从懻撉鍐?#xff0c;因?yàn)榇蠖鄶?shù)時(shí)候它們都不是必需的,但是Java代理不是這種情況。 為打包為Java歸檔(或簡(jiǎn)稱JAR)文件的Java代理定義了以下屬性:

清單屬性 描述
初級(jí)班 在JVM啟動(dòng)時(shí)指定了代理時(shí),此屬性定義Java代理類:包含premain方法的類。 在JVM啟動(dòng)時(shí)指定代理時(shí),此屬性是必需的。 如果該屬性不存在,JVM將中止。
代理級(jí) 如果實(shí)現(xiàn)支持在JVM啟動(dòng)后的某個(gè)時(shí)間啟動(dòng)Java代理的機(jī)制,則此屬性指定代理類:包含agentmain方法的類。 此屬性是必需的,如果不存在該代理,則不會(huì)啟動(dòng)代理。
引導(dǎo)類路徑 引導(dǎo)類加載器要搜索的路徑列表。 路徑代表目錄或庫。
可以重新定義類 true或false值,不區(qū)分大小寫,并且定義是否具有重新定義此代理所需的類的能力。 此屬性是可選的,默認(rèn)值為false 。
可以重新轉(zhuǎn)換類 true或false值,不區(qū)分大小寫,并且定義是否具有重新轉(zhuǎn)換此代理所需的類的能力。 此屬性是可選的,默認(rèn)值為false 。
可以設(shè)置本機(jī)方法前綴 true或false值,不區(qū)分大小寫,并且定義是否可以設(shè)置此代理所需的本機(jī)方法前綴。 此屬性是可選的,默認(rèn)值為false 。

有關(guān)更多詳細(xì)信息,請(qǐng)隨時(shí)查閱專用于Java代理和工具的官方文檔 。

3. Java代理和規(guī)范

Java代理的檢測(cè)功能確實(shí)是無限的。 最引人注意的包括但不限于:

  • 能夠在運(yùn)行時(shí)重新定義類。 重新定義可能會(huì)更改方法主體,常量池和屬性。 重新定義不得添加,刪除或重命名字段或方法,更改方法的簽名或更改繼承。
  • 能夠在運(yùn)行時(shí)重新轉(zhuǎn)換類。 重新轉(zhuǎn)換可能會(huì)更改方法主體,常量池和屬性。 重新轉(zhuǎn)換不得添加,刪除或重命名字段或方法,更改方法的簽名或更改繼承。
  • 通過允許重命名使用前綴來修改本機(jī)方法解析的失敗處理的能力。

請(qǐng)注意,在應(yīng)用轉(zhuǎn)換或重新定義后,不會(huì)檢查,驗(yàn)證和安裝重新轉(zhuǎn)換或重新定義的類字節(jié)碼。 如果生成的字節(jié)碼錯(cuò)誤或不正確,則將引發(fā)異常,這可能會(huì)使JVM完全崩潰。

4.編寫您的第一個(gè)Java代理

在本節(jié)中,我們將通過實(shí)現(xiàn)我們自己的類轉(zhuǎn)換器來編寫一個(gè)簡(jiǎn)單的Java代理。 話雖如此,使用Java代理的唯一缺點(diǎn)是,為了完成或多或少的有用轉(zhuǎn)換,需要直接字節(jié)碼操作技能。 而且,不幸的是,Java標(biāo)準(zhǔn)庫沒有提供任何API(至少是有文檔的API)來使這些字節(jié)碼操作成為可能。

為了填補(bǔ)這一空白,富有創(chuàng)造力的Java社區(qū)提出了一些優(yōu)秀的,非常成熟的庫,例如Javassist和ASM ,僅舉幾例。 在這兩種方法中,Javassist使用起來更簡(jiǎn)單,這就是為什么它成為我們將要用作字節(jié)碼操作解決方案的原因。 到目前為止,這是我們第一次無法在Java標(biāo)準(zhǔn)庫中找到合適的API,除了使用社區(qū)提供的API之外別無選擇。

我們將要處理的示例相當(dāng)簡(jiǎn)單,但它取自于實(shí)際的用例。 假設(shè)我們要捕獲Java應(yīng)用程序打開的每個(gè)HTTP連接的URL。 有很多方法可以通過直接修改Java源代碼來做到這一點(diǎn),但讓我們假設(shè)由于許可證策略或其他原因?qū)е略创a不可用。 打開HTTP連接的類的典型示例如下所示:

public class SampleClass {public static void main( String[] args ) throws IOException {fetch("http://www.google.com");fetch("http://www.yahoo.com");}private static void fetch(final String address) throws MalformedURLException, IOException {final URL url = new URL(address); final URLConnection connection = url.openConnection();try( final BufferedReader in = new BufferedReader(new InputStreamReader( connection.getInputStream() ) ) ) {String inputLine = null;final StringBuffer sb = new StringBuffer();while ( ( inputLine = in.readLine() ) != null) {sb.append(inputLine);} System.out.println("Content size: " + sb.length());}} }

Java代理非常適合解決此類挑戰(zhàn)。 我們只需要定義一個(gè)轉(zhuǎn)換器,即可通過注入代碼以將輸出生成到控制臺(tái)來稍微修改sun.net.www.protocol.http.HttpURLConnection構(gòu)造函數(shù)。 聽起來很嚇人,但是使用ClassFileTransformer和Javassist非常簡(jiǎn)單。 讓我們看一下這樣的轉(zhuǎn)換器實(shí)現(xiàn):

public class SimpleClassTransformer implements ClassFileTransformer {@Overridepublic byte[] transform( final ClassLoader loader, final String className,final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain,final byte[] classfileBuffer ) throws IllegalClassFormatException {if (className.endsWith("sun/net/www/protocol/http/HttpURLConnection")) {try {final ClassPool classPool = ClassPool.getDefault();final CtClass clazz = classPool.get("sun.net.www.protocol.http.HttpURLConnection");for (final CtConstructor constructor: clazz.getConstructors()) {constructor.insertAfter("System.out.println(this.getURL());");}byte[] byteCode = clazz.toBytecode();clazz.detach();return byteCode;} catch (final NotFoundException | CannotCompileException | IOException ex) {ex.printStackTrace();}}return null;} }

ClassPool和所有CtXxx類( CtClass , CtConstructor )都來自Javassist庫。 我們所做的轉(zhuǎn)換是非常幼稚的,但此處僅用于演示目的。 首先,由于我們僅對(duì)HTTP通信感興趣,因此sun.net.www.protocol.http.HttpURLConnection是來自標(biāo)準(zhǔn)Java庫的類。

請(qǐng)注意,而不是“。” 分隔符, className帶有“ /”。 其次,我們尋找HttpURLConnection類,并通過注入System.out.println(this.getURL());修改其所有構(gòu)造函數(shù)System.out.println(this.getURL()); 最后的聲明。 最后,我們返回了該類轉(zhuǎn)換后的版本的新字節(jié)碼,因此它將由JVM使用,而不是原始版本。

這樣,Java代理的premain方法的作用就是將SimpleClassTransformer類的實(shí)例SimpleClassTransformer到檢測(cè)上下文中:

public class SimpleAgent {public static void premain(String agentArgs, Instrumentation inst) {final SimpleClassTransformer transformer = new SimpleClassTransformer();inst.addTransformer(transformer);} }

而已。 看起來很容易,同時(shí)又有些令人恐懼。 為了完成Java代理,我們必須提供適當(dāng)?shù)?strong>MANIFEST.MF,以便JVM能夠選擇正確的類。 這是必需屬性的相應(yīng)最小集合(有關(guān)更多詳細(xì)信息,請(qǐng)參閱Java Agent Basics部分):

Manifest-Version: 1.0 Premain-Class: com.javacodegeeks.advanced.agent.SimpleAgent

這樣一來,首先的Java代理就準(zhǔn)備好進(jìn)行一場(chǎng)真正的戰(zhàn)斗。 在本教程的下一部分中,我們將介紹一種與Java應(yīng)用程序一起運(yùn)行Java代理的方法。

5.運(yùn)行Java代理

從命令行運(yùn)行時(shí),可以使用具有以下語義的-javaagent參數(shù)將Java代理傳遞到JVM實(shí)例:

-javaagent:<path-to-jar>[=options]

其中<path-to-jar>是查找Java代理JAR歸檔文件的路徑,而options包含可以通過agentArgs參數(shù)更準(zhǔn)確地傳遞給Java代理的其他選項(xiàng)。 例如,從編寫您的第一個(gè)Java代理 (使用Java 7版本)部分運(yùn)行我們的Java代理的命令行如下所示(假定代理JAR文件位于當(dāng)前文件夾中):

java -javaagent:advanced-java-part-15-java7.agents-0.0.1-SNAPSHOT.jar

與advanced-java-part-15-java7.agents-0.0.1-SNAPSHOT.jar Java代理一起運(yùn)行SampleClass類時(shí),該應(yīng)用程序?qū)⒃诳刂婆_(tái)上打印所有URL( Google和Yahoo! )。嘗試使用HTTP協(xié)議進(jìn)行訪問(其次是Google和Yahoo!搜索主頁的內(nèi)容大小):

http://www.google.com Content size: 20349 http://www.yahoo.com Content size: 1387

在未指定Java代理的情況下運(yùn)行相同的SampleClass類將僅在控制臺(tái)上輸出內(nèi)容大小,而不輸出URL(請(qǐng)注意,內(nèi)容大小可能會(huì)有所不同):

Content size: 20349 Content size: 1387

JVM使運(yùn)行Java代理變得簡(jiǎn)單。 但是,請(qǐng)注意,任何錯(cuò)誤或不正確的字節(jié)碼生成都可能使JVM崩潰,并可能丟失此時(shí)您的應(yīng)用程序可能保存的重要數(shù)據(jù)。

6.接下來

到最后,高級(jí)Java教程也結(jié)束了。 希望您發(fā)現(xiàn)它是有用,實(shí)用和有趣的。 有許多主題尚未涵蓋,但是非常歡迎您繼續(xù)深入探討Java語言,平臺(tái),生態(tài)系統(tǒng)和不可思議的社區(qū)的奇妙世界。 祝好運(yùn)!

7.下載源代碼

您可以在此處下載本課程的源代碼: advanced-java-part-15

翻譯自: https://www.javacodegeeks.com/2015/09/java-agents.html

java代理模式

總結(jié)

以上是生活随笔為你收集整理的java代理模式_Java代理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。