介紹
在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等
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/144540.html
標籤:Java
上一篇:在 Alibaba 廣受喜愛的“Java 突擊寶典”簡直太牛了
下一篇:Java容器集合經典面試題集
