java instrumentation &JVMTI

Java Instrumentation (參考:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/java

簡介:數組

使用Instrumentation,開發者能夠構建獨立於應用程序的代理程序,用來檢測和協助運行在JVM上的程序,甚至可以替換和修改某些類的定義tomcat

遇到問題:ide

運行在tomcat容器中的應用程序,因爲main方法是在Bootstrap.jar中容器啓動時執行的,函數

Agent-class方式在替換的方法中只獲取到tomcat相關jar包中的一些類:spa

上圖中是在transform方法中打印出來的,這個方法會在二中介紹命令行

具體介紹:3d

下面是兩種方式PreMain 和 agentMain,分別爲main函數執行以前,和執行以後的操做代理

一:PreMaincode

main函數執行之間,掃描判斷特定的類,而後以字節數組的方式加載代理類的字節碼文件,替換目標類:

示例代碼:

public class TransClass {
    public int getNumber() {
        return 2;
    }
}
TransClass

 

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 = "D://AOP//TransClass.class.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();
        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 {
        if (!className.equals("TransClass")) {
            return null;
        }
        return getBytesFromFile(classNumberReturns2);
    }
} 
Transformer
public class TestMainInJar {
    public static void main(String[] args) {
        System.out.println(new TransClass().getNumber());
    }
}
TestMainInJar
import java.lang.instrument.UnmodifiableClassException;
import java.lang.instrument.Instrumentation;
public class Premain {
public static void premain(String agentArgs, Instrumentation inst)
    throws ClassNotFoundException, UnmodifiableClassException {
        inst.addTransformer(new Transformer());
    }
} 
Premain
Manifest-Version: 1.0
Premain-Class: Premain
MANIFEST.MF

步驟:

1. TransClass.java 中 改爲 return 2; 以後編譯生成的TransClass.class更名爲TransClass.class.2 ,以防止跟原TransClass.class重名

2. 打包:jar -cvf0 TestInstrument1.jar TransClass.class Transformer.class TestMainInJar.class Premain.class

            打好jar後替換manifest.mf文件,(沒找到怎麼把這個文件直接打進jar包)

3. 命令行執行

    1)java -javaagent:TestInstrument1.jar -cp TestInstrument1.jar TestMainInJar

    2)java -cp TestInstrument1.jar TestMainInJar

 (TransClass.class.2 和 TestInstrument1.jar 須要放置到  D:\AOP 目錄下)

    用第一個命令執行的時候,會把TransClass.class.2的內容加載進來替換jar包中的TransClass.class,而第二個命令是正常執行jar包中的TransClass.class

二: Agent-class

import java.lang.instrument.Instrumentation;

public class LoadedAgent {
    @SuppressWarnings("rawtypes")
    public static void agentmain(String args, Instrumentation inst){
        Class[] classes = inst.getAllLoadedClasses();
    //inst.addTransformer(new Transformer());
        for(Class cls :classes){
            System.out.println(cls.getName());
        }
    }
}
LoadedAgent

 

public class TargetVM {
    public static void main(String[] args) throws InterruptedException{
        while(true){
            Thread.sleep(1000);
        }
    }
}
TargetVM
import java.io.IOException;

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;

public class Test {
    public static void main(String[] args) throws AttachNotSupportedException,
            IOException, AgentLoadException, AgentInitializationException {
        VirtualMachine vm = VirtualMachine.attach("1244");
        vm.loadAgent("D:/AOP/agentmain/agentAop.jar");

    }

}
Test
Manifest-Version: 1.0
Agent-Class: LoadedAgent
MANIFEST.MF

步驟:

    1. 將LoadedAgent.class 和 Manifest.mf打進jar包

    2. 執行TargetVM.class 獲取進程號PID

    3. 執行Test.class pid做爲參數

  

注意:

    1. 編譯時須要用到jdk中lib目錄下的tools.jar  javac -cp tools.jar Test.java

    2. 執行命令(1244 是進程號) :java -classpath "D:/AOP/agentmain/tools.jar" Test 1244    

相關文章
相關標籤/搜索