介紹 在JDK1.5以後,我們可以使用agent技術構建一個獨立於應用程式的代理程式(即為Agent),用來協助監測、運行甚至替換其他JVM上的程式。使用它可以實現虛擬機級別的AOP功能。Agent分為兩種,一種是在主程式之前運行的Agent,一種是在主程式之後運行的Agent(前者的升級版,1.6 ...
介紹
在JDK1.5以後,我們可以使用agent技術構建一個獨立於應用程式的代理程式(即為Agent),用來協助監測、運行甚至替換其他JVM上的程式。使用它可以實現虛擬機級別的AOP功能。Agent分為兩種,一種是在主程式之前運行的Agent,一種是在主程式之後運行的Agent(前者的升級版,1.6以後提供)。
使用
主程式運行之前的代理程式
創建代理類
public class MyPreMainAgent {
//方法名和參數都是固定的 premain表示在主程式運行之前運行
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("PreMain start");
System.out.println(agentArgs);
System.out.println(inst);
}
}
Instrumentation是java1.5新提供的類,它提供在運行時重新載入某個類的的class文件的api。
public interface Instrumentation {
/**
* 添加一個轉換器Transformer,之後的所有的類載入都會被Transformer攔截。
* ClassFileTransformer類是一個介面,使用時需要實現它,該類只有一個方法,該方法傳遞類的信息,返回值是轉換後的類的位元組碼文件。
*/
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
/**
* 對JVM已經載入的類重新觸發類載入。使用的就是上面註冊的Transformer。
* 該方法可以修改方法體、常量池和屬性值,但不能新增、刪除、重命名屬性或方法,也不能修改方法的簽名
*/
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
/**
*此方法用於替換類的定義,而不引用現有的類文件位元組,就像從源代碼重新編譯以進行修複和繼續調試時所做的那樣。
*在要轉換現有類文件位元組的地方(例如在位元組碼插裝中),應該使用retransformClasses。
*該方法可以修改方法體、常量池和屬性值,但不能新增、刪除、重命名屬性或方法,也不能修改方法的簽名
*/
void redefineClasses(ClassDefinition... definitions)throws ClassNotFoundException, UnmodifiableClassException;
/**
* 獲取一個對象的大小
*/
long getObjectSize(Object objectToSize);
/**
* 將一個jar加入到bootstrap classloader的 classpath里
*/
void appendToBootstrapClassLoaderSearch(JarFile jarfile);
/**
* 獲取當前被JVM載入的所有類對象
*/
Class[] getAllLoadedClasses();
}
maven打包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
//指明提供代理功能的類
<Premain-Class>com.imooc.myagent.MyPreMainAgent</Premain-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
客戶端調用
在另一個項目中使用這個jar包。
public class Client {
public static void main(String[] args) {
System.out.println("main");
}
}
添加虛擬機啟動參數
-javaagent:jar包路徑=hah
輸出結果為
PreMain start
hah
sun.instrument.InstrumentationImpl@27c170f0
main
結果符合預期,在main方法執行前執行了premain.
主程式運行之後的代理程式
創建代理類
public class MyAgentMainAgent {
//表示在main方法執行之後執行
public static void agentmain(String agentArgs, Instrumentation inst) {
System.out.println("AgentMain start");
System.out.println(agentArgs);
System.out.println(inst);
//獲取所有已載入的類
Class[] allLoadedClasses = inst.getAllLoadedClasses();
for (Class loadedClass : allLoadedClasses) {
System.out.println(loadedClass);
}
}
}
maven打包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
//指明提供代理功能的類
<Agent-Class>com.imooc.myagent.MyAgentMainAgent</Agent-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
客戶端調用
在另一個項目中使用這個jar包。在主程式運行之後載入,我們沒辦法在主程式中調用,只能使用輔助程式然後和主程式通信,這裡要用到attach機制。
Attach API是Sun公司提供的一套擴展API,用來向目標JVM"附著"(Attach)代理工具程式的。有了它,開發者可以方便的監控一個JVM,運行一個外加的代理程式,Sun JVM Attach API功能上非常簡單,僅提供瞭如下幾個功能:
- 列出當前所有的JVM實例描述
- Attach到其中一個JVM上,建立通信管道
- 讓目標JVM載入Agent
jdk提供的jstack,jps功能就是使用該機制實現的。
主程式為
public class Client {
public static void main(String[] args) {
while (true) {
System.out.println("now:" + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
讓主程式一直在跑。
輔助程式為
public class Client2 {
public static void main(String[] args)
throws IOException,
AttachNotSupportedException,
AgentLoadException,
AgentInitializationException {
//代理程式的jar包位置
String agentPath = "D:\\java\\code_resp\\IdeaProjects\\myagent\\target\\myagent-1.0-SNAPSHOT.jar";
//獲取所有實例
List<VirtualMachineDescriptor> descriptorList = VirtualMachine.list();
for (VirtualMachineDescriptor descriptor : descriptorList) {
//判斷如果是主程式,就載入代理程式
if (descriptor.displayName().equals(Client.class.getName())) {
VirtualMachine virtualMachine = VirtualMachine.attach(descriptor);
virtualMachine.loadAgent(agentPath, "hello");
}
}
}
}
輸出結果為
now:Sun Jul 12 15:51:55 CST 2020
now:Sun Jul 12 15:52:05 CST 2020
now:Sun Jul 12 15:52:15 CST 2020
AgentMain start
hello
sun.instrument.InstrumentationImpl@5e84c484
class com.imooc.myagent.MyAgentMainAgent
class com.imooc.sourcecode.java.javaagent.test2.Client
class com.intellij.rt.execution.application.AppMainV2$1
class com.intellij.rt.execution.application.AppMainV2
class com.intellij.rt.execution.application.AppMainV2$Agent
class sun.nio.cs.Surrogate
class sun.nio.cs.Surrogate$Parser
class sun.nio.cs.ISO_8859_1$Encoder
...
...
...
結果符合預期,列印了所有已載入的類。
使用場景
- apm:(Application Performance Management)應用性能管理。pinpoint、cat、skywalking等都基於Instrumentation實現
- idea的HotSwap、Jrebel等熱部署工具
- 應用級故障演練
- Java診斷工具Arthas、Btrace等