002、从零开始写调用链监控-体验Instrumentation

1、javaagent可以做什么?

前面我们搭建了一个hello javaagent的项目,也算是踏出了从零开始写调用链监控的第一步,但是这个项目可能太简单,完全不知道javaagent能做啥,总不是为了在项目启动的时候打印一条日志吧,当然不是,接下来我们用Instrumentation对象来实现class的转换,体验一下javaagent能做什么。

2、项目扩展

项目结构:

002、从零开始写调用链监控-体验Instrumentation_第1张图片
1566572151802.png

体验说明:

TestSnifferAgent里面TestInstrumentation(System.out.println(1);)的被SnifferAgent里面的Instrumentation转换为TestInstrumentation.class.2(TestInstrumentation(System.out.println(2);)编译后的字节码文件)的执行,所以原本打印1的操作,变成了打印2。

关键代码:

SnifferAgent.java:

javaagent的入口方法,类似main方法

public class SnifferAgent {
    /**
     * 在方法在main方法之前执行,和main方法同Jvm、ClassLoader、Security policy和Context
     *
     * @param agentOps javaagent入参
     * @param inst     对class进行字节码加强的代理实例
     */
    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("hello javaagent!this is premain!");
        inst.addTransformer(new Transformer());
    }

    static class Transformer implements ClassFileTransformer {
        public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined
                , ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if (className.equals("cn/gy4j/monitor/test/sniffer/agent/TestInstrumentation")) {
                // TestInstrumentation.class.2(TestInstrumentation的print方法为System.out.println(2);的class文件)
                InputStream inputStream = loader.getResourceAsStream("TestInstrumentation.class.2");
                try {
                    ByteArrayOutputStream output = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024 * 4];
                    int n = 0;
                    while (-1 != (n = inputStream.read(buffer))) {
                        output.write(buffer, 0, n);
                    }
                    return output.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
}

TestSnifferAgent.java

public class TestSnifferAgent {
    /**
     * 测试SnifferAgent的main方法
     * jvm配置:-javaagent:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("hello javaagent!this is main!");
        new TestInstrumentation().print();
    }
}

TestInstrumentation.java

public class TestInstrumentation {
    public void print() {
        System.out.println(1);
    }
}

TestInstrumentation.class.2

即修改TestInstrumentation的System.out.println(1);为System.out.println(2);编译后产生的TestInstrumentation.class,重命名为TestInstrumentation.class.2

3、测试验证

第一步:编译打包

mvn clean package

获取路径:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar

第二步:测试验证

1、不加jvm参数执行结果如下:
002、从零开始写调用链监控-体验Instrumentation_第2张图片
1566572727524.png
2、加入jvm参数(使用前面copy的jar路径):
-javaagent:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
002、从零开始写调用链监控-体验Instrumentation_第3张图片
1566529887800.png
执行TestSnifferAgent的main方法:
002、从零开始写调用链监控-体验Instrumentation_第4张图片
1566572841286.png
3、测试结果说明:

不加入jvm参数,即执行new TestInstrumentation().print();的时候,就是当前类的System.out.println(1);,从而输出1,当引入javaagent的时候,通过Instrumentation的实例方法inst.addTransformer(new Transformer());对类TestInstrumentation进行了转换,换成了TestInstrumentation.class.2的字节码,所以实际jvm里面的TestInstrumentation的实例对象方法变成了System.out.println(2),这就是javaagent的Instrumentation可以做的事情,可以对jvm里面的class进行替换、加强等,实际字节码的加强我们不会通过这样的方式进行转换,现在字节码加强的框架不少,例如Javaasist、cglib、bytebuddy等,下一节,我们就采用和skywalking的字节码加强框架bytebuddy进行框架的扩展。

4、源码地址

https://github.com/gy4j/gy4j-monitor/tree/002-javaagent-instrumentation

你可能感兴趣的:(002、从零开始写调用链监控-体验Instrumentation)