java高階-動態注入替換類Instrumentation
阿新 • • 發佈:2018-12-13
介紹
- 利用java.lang.instrument(容器類) 做動態 Instrumentation(執行容器) 是 Java SE 5 的新特性。
- 使用 Instrumentation,開發者可以構建一個獨立於應用程式的代理程式(Agent),用來監測和協助執行在 JVM 上的程式,甚至能夠替換和修改某些類的定義。
- 這個功能為虛擬機器監控提供了支撐。
基本用法
1. 編寫 premain 函式
編寫一個 Java 類,包含如下兩個方法當中的任何一個
public static void premain(String agentArgs, Instrumentation inst); [1] public static void premain(String agentArgs); [2] 其中,[1] 的優先順序比 [2] 高,將會被優先執行([1] 和 [2] 同時存在時,[2] 被忽略)。 agentArgs 是 premain 函式得到的程式引數,隨同 “– javaagent”一起傳入。與 main 函式不同的是,這個引數是一個字串而不是一個字串陣列,如果程式引數有多個,程式將自行解析這個字串。 Inst 是一個 java.lang.instrument.Instrumentation 的例項,由 JVM 自動傳入。java.lang.instrument.Instrumentation 是 instrument 包中定義的一個介面,也是這個包的核心部分,集中了其中幾乎所有的功能方法,例如類定義的轉換和操作等等。
2. jar 檔案打包
將這個 Java 類打包成一個 jar 檔案,並在其中的 manifest 屬性當中加入” Premain-Class”來指定步驟 1 當中編寫的那個帶有 premain 的 Java 類。(可能還需要指定其他屬性以開啟更多功能)
3. 執行
用如下方式執行帶有 Instrumentation 的 Java 程式:
java -javaagent:jar 檔案的位置 [= 傳入 premain 的引數 ]
案例
1,編寫正常的類
主類
package com.yixiu.javabase.modules.dynamic.instrumentation.demo1; public class TestMainInJar { public static void main(String[] args) { System.out.println(new TransClass().getNumber()); } }
工具類
package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
public class TransClass {
public int getNumber() {
return 1;
}
}
2,編寫代理類
新增Premain類
package com.yixiu.javabase.modules.dynamic.instrumentation.demo1; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; public class Premain { public static void premain(String agentArgs,Instrumentation inst) throws ClassNotFoundException,UnmodifiableClassException { System.out.println("Premain"); inst.addTransformer(new Transformer()); } }
類檔案轉化器:
注意:實際測試時,要去掉中文,否則javac編譯報錯。
package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
class Transformer implements ClassFileTransformer {
// 注意是完整路徑
public static final String classNumberReturns2 = "E:\\Git\\Java\\JavaLearning\\src\\main\\java\\com\\yixiu\\javabase\\modules\\dynamic\\instrumentation\\demo1\\classes\\TransClass2.java.2";
public static byte[] getBytesFromFile(String fileName) {
try {
// precondition
File file = new File(fileName);
InputStream is = new FileInputStream(file);
long length = file.length();
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset <bytes.length
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
if (offset < bytes.length) {
throw new IOException("Could not completely read file "
+ file.getName());
}
is.close();
System.out.println("class length:" + String.valueOf( bytes.length ) );
return bytes;
} catch (Exception e) {
System.out.println("error occurs in _ClassTransformer!"
+ e.getClass().getName());
return null;
}
}
public byte[] transform(ClassLoader l, String className, Class<?> c,
ProtectionDomain pd, byte[] b) throws IllegalClassFormatException {
System.out.println("load:" + className );
// 注意,類名稱是完整的路徑 load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TransClass
if(!className.contains("TransClass") ) {
return null;
}
// if (!className.equals("TransClass")) {
// return null;
// }
return getBytesFromFile(classNumberReturns2);
}
}
3,編譯,打包
建立目錄classes,編譯所有檔案到classes中
javac -d .\classes .\*.java
切換目錄
cd classes
打包
jar -cvf my.jar .\*
使用rar開啟my.jar,修改MANIFEST.MF,新增
Premain-Class: com.yixiu.javabase.modules.dynamic.instrumentation.demo1.Premain
4,實際要動態注入替換的類
javac編譯時檢查(字尾名是.java,類名和檔名相同)
要新增的代替類,必須編譯為位元組碼後才可以新增
先修改類TransClass的返回值,編譯後,將檔名重新命名為TransClass2.java.2,和jar包放在同一個目錄中
package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
public class TransClass {
public int getNumber() {
return 2;
}
}
5,編譯修改的包,執行
mentation\demo1\classes>java -javaagent:my.jar -cp my.jar com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar
輸出如下:
E:\Git\Java\JavaLearning\src\main\java\com\yixiu\javabase\modules\dynamic\instru
mentation\demo1\classes>java -javaagent:my.jar -cp my.jar com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar
Premain
load:java/lang/invoke/MethodHandleImpl
load:java/lang/invoke/MethodHandleImpl$1
load:java/lang/invoke/MethodHandleImpl$2
load:java/util/function/Function
load:java/lang/invoke/MethodHandleImpl$3
load:java/lang/invoke/MethodHandleImpl$4
load:java/lang/ClassValue
load:java/lang/ClassValue$Entry
load:java/lang/ClassValue$Identity
load:java/lang/ClassValue$Version
load:java/lang/invoke/MemberName$Factory
load:java/lang/invoke/MethodHandleStatics
load:java/lang/invoke/MethodHandleStatics$1
load:sun/misc/PostVMInitHook
load:sun/usagetracker/UsageTrackerClient
load:java/util/concurrent/atomic/AtomicBoolean
load:sun/usagetracker/UsageTrackerClient$1
load:sun/usagetracker/UsageTrackerClient$4
load:sun/usagetracker/UsageTrackerClient$2
load:java/lang/ProcessEnvironment
load:java/lang/ProcessEnvironment$NameComparator
load:java/lang/ProcessEnvironment$EntryComparator
load:java/util/Collections$UnmodifiableMap
load:java/lang/ProcessEnvironment$CheckedEntrySet
load:java/util/HashMap$EntrySet
load:java/lang/ProcessEnvironment$CheckedEntrySet$1
load:java/util/HashMap$EntryIterator
load:java/util/HashMap$HashIterator
load:java/lang/ProcessEnvironment$CheckedEntry
load:sun/usagetracker/UsageTrackerClient$3
load:java/io/FileOutputStream$1
load:sun/launcher/LauncherHelper
load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TestMainInJar
load:sun/launcher/LauncherHelper$FXHelper
load:java/lang/Class$MethodArray
load:java/lang/Void
load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TransClass
class length:309
2
load:java/lang/Shutdown
load:java/lang/Shutdown$Lock
參考
- Java程式設計的動態特性, 從Reflection到Runtime Class Transformation https://blog.csdn.net/iteye_12751/article/details/82550531
- Instrumentation 新功能 https://www.ibm.com/developerworks/cn/java/j-lo-jse61/
- Java Instrumentation https://blog.csdn.net/DorMOUSENone/article/details/81781131