您好,登錄后才能下訂單哦!
本篇內容介紹了“不用Jar包的Agent的優點有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
提起 JavaAgent,很多人都說幾句,就像古龍武俠小說里的「孔雀翎」,威力很大,江湖上都是它的傳說。但真的見識過的人并沒幾個。
JavaAgent 雖說沒這么神秘,但也一直給人曲高和寡的感覺,除了一些中間件產品、大型的框架中使用外,在業務中一直很少出現。
原因可能有很多,一來是可能確實不需要,再者需要開發獨立的 Agent Jar 文件,在 Jar 內對類的 transform 開發也并不容易。
我們知道,無論是啟動時的 Java Agent,還是運行時的動態 attach 到遠程JVM, 都是為了拿到 Instrument,對 class 的字節碼進行修改。這么底層的東西,當然使用起來讓人不太容易下手。
不過就像機器語言不方便,人們發明了匯編語言,又發現了高級語言。對于字節碼的操作也類似,人們覺得直接操作字節碼有難度,而且需要深入理解 JVM 規范,具體什么位置多少字節代表啥,這不是一般人喜歡的,于是 ASM 框架出現了;但還是有規范的影子,不太「高級」,于是又出現了Javassist 這一類的「高級」工具。
我們今天要說的這個工具,和 Javassist 類似,都提供了更高層的API,來操作class,對普通程序員更友好,除此之外呢?
就像今天人們購物、讀書等,都更相信專業的平臺、或者專家的推薦,像XX嚴選,XX讀書會推薦。今天說的這個工具是Duke 的推薦,對,就是它, Java 的吉祥物,這個小胖子。今天的這個工具在 2015年被 Oracle 評選為「Duke's Choice award」。
除了Duke,框架也得到了眾多開發者的認可,每年有七千多萬次的下載。
這個工具是:Bytebuddy。
從名字你就看的出來,它立志要做字節碼的好伙伴。所以在很多開源框架里也能看到它的身影。
既然已經有了不少的工具, byteBuddy能帶來什么不一樣呢?
除了API 上的簡潔易操作,官方自己也大字強調了運行時動態的「代碼生成和字節碼操作」,不需要再借助 Java 編譯器。
來看看官網是怎么自我介紹的,后面再附上幾個代碼片段,就能很快 Get 到了。
官網:https://bytebuddy.net/
Byte Buddy is a code generation and manipulation library for creating and modifying Java classes during the runtime of a Java application and without the help of a compiler. Other than the code generation utilities that ship with the Java Class Library, Byte Buddy allows the creation of arbitrary classes and is not limited to implementing interfaces for the creation of runtime proxies. Furthermore, Byte Buddy offers a convenient API for changing classes either manually, using a Java agent or during a build.
閱讀理解開始。重點你一定會看到了:
「code generation」
「creating and modifying Java classes」
作者貼心的加了一段小字來描述框架的優勢。選重點的說就是:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
不需要理解字節碼,也不需要理解class 文件格式
API 非侵入,設計簡潔易懂
高度可定制,可以任意自定義
我自己認為該把這點也加上,不寫 Java Agent 也可以 Attach 到 JVM 上,把 ByteBuddy 自己當成一個Agent,運行時直接install。Cool。
不寫JVM Agent 也能對類攔截和修改,我們來認識下揭開字節碼修改黑魔法的家伙。
為了對 Class 進行一些操作,我們一般都離不了 JVM Agent。不管是啟動時直接連接,還是運行時動態的 Attach到對應的JVM 上,都需要 Agent。也就是我們熟悉的premain 和 agentmain 的觸發入口,通過它們,我們才能拿到 Instrumentation,從而進行 transform和 redeine。
但這個東西的使用,給人總是「陽春白雪」的感覺,讓人覺得是黑魔法一樣,一般不會輕易嘗試使用。
有了ByteBuddy,就不用再羨慕一些框架的「運行時增強」,「動態織入」等等,都可以實現。
如何上手呢?
只需要下載 Jar 文件或者 Maven 添加依賴之后就能狂奔了。
比如官方的這個 HelloWorld
Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) .method(ElementMatchers.named("toString")) .intercept(FixedValue.value("Hello World!")) .make() .load(getClass().getClassLoader()) .getLoaded(); assertThat(dynamicType.newInstance().toString(), is("Hello World!"));
直接把 Object 的 toString 方法改寫了。
再比如我們可以在開發 Java Agent的時候使用這個伙計
public static void premain(String args, Instrumentation inst) { AgentBuilder agentBuilder = new AgentBuilder.Default(); AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() { public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) { String className = typeDescription.getCanonicalName(); builder = builder.method(ElementMatchers.any())//匹配任意方法 .intercept(MethodDelegation.to(new SimplePackageInstanceMethodInterceptor())); return builder; } }; agentBuilder = agentBuilder.type(ElementMatchers.nameStartsWith("com.example.hello.sample")).transform(transformer); agentBuilder.installOn(inst); }
在類里進行攔截匹配的時候,可以通過類名來限定,同時以不同的模式去匹配方法名等,這里的ElementMatchers可以用在類名與方法名等匹配場景中
//ElementMatchers.named("abc") // 特定名稱的方法 //ElementMatchers.nameStartsWith("hello") // 以什么開頭的 //ElementMatchers.nameEndsWith("test") // 以什么結尾的
我們看到前面的代碼中 agentBuilder.installOn(inst);
通過 JavaAgent的Instrument 進行類修改。
AgentBuilder 還提供了一個神奇的方法:
agentBuilder.installOnByteBuddyAgent();
這樣無須提供 Jar 文件也一樣能實現運行時增強。不過需要注意,這樣使用時,一定要先執行這行代碼,這也是其實現的秘密:
ByteBuddyAgent.install();
因為 ByteBuddy 自己做為一個 Jar 也 Attach ,然后再將其它后續的增強代碼加入,像不像「特洛伊木馬」 :)
另外, ByteBuddy 還支持類似于 AOP 的 Advice實現,在攔截指定方法后可以實現OnMethodEnter 和 OnMethodExit 的控制,在這其中,可以完成繞過用戶代碼,執行自定義內容的邏輯。
咱們在開始的時候,還提到了代碼的生成。這在 ByteBuddy 看來也是易如反掌。
和上面的代碼一樣,先要拿到 AgentBuilder,之后在執行 tranform的時候,直接指定方法名,以及對應的參數,訪問控制符等。
DynamicType.Builder.MethodDefinition.ExceptionDefinition<?> hello = builder.defineMethod(methodName, types, Visibility.PUBLIC) .withParameters(m.getParameters().asTypeList());
再比如在運行時給一個方法增加注解,
builder.method(ElementMatchers.named("methodName")).intercept(SuperMethodCall.INSTANCE) .annotateMethod(AnnotationDescription.Builder.ofType(TestAnnotation.class) .define("testValue", 123).build());
是不是功能很強大?
“不用Jar包的Agent的優點有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。