JVM

JavaAgent

2022-06-08  本文已影响0人  越狱的灵感

前言

java agent即为java.lang.instrument。官方解释

1.png
可以理解为字节码增强技术。一般结合字节码修改框架,比如Javassist,ASM(门槛比较高)等框架一起使用。比如pinpoint,skywalking,arthas等做全链路监控或者服务分析。

技术原理

参考你假笨大佬的图


2.png

写个DEMO

1,首先引入javassist字节码编辑包工具

<dependency>
   <groupId>org.javassist</groupId>
   <artifactId>javassist</artifactId>
</dependency>

2,编写一个待注入服务,并打包成jar包

//--接口
public class JobService {
    public void exec(String params) {
        System.out.println("JobService start job.....");
        System.out.println("job params: " + params + ".");
        System.out.println("JobService end job.....");
    }
}
//--调用接口主函数
public class JavaAgentMainTest {
    public static void main(String[] args) {
        test1();
    }
 
    private static void test1() {
        JobService jobService = new JobService();
        jobService.exec("a=1;b=2;");
    }
}

3,编译打包成jar


3.png

注意第4步需要在 src/main/resources/目录下生成MANIFEST.MF文件,记得在Main-Class后面需要有一个空行。


4.png
打包
5.png

4,检测jar包是否打包正常
java -jar examples.jar


6.png

5,编写javaagent插件jar包
给JobService.exec添加耗时统计的代码

#字节码编辑类
public class TimeConsumerTransformer implements ClassFileTransformer {
 
    final static String startTime = "\nlong startTime = System.nanoTime();\n";
    final static String endTime = "\nlong endTime = System.nanoTime();\n";
 
    final static Map<String, List<String>> methodMap = new HashMap<>();
 
    public TimeConsumerTransformer() {
        //--把代理的类全部加到待处理链表中
        addMethod("com.whg.ex.deeplearnv2.examples.javaagents.JobService", "exec");
    }
 
    private TimeConsumerTransformer addMethod(String className, String methodName) {
        List<String> list = methodMap.computeIfAbsent(className, cn -> new ArrayList<>());
        list.add(methodName);
        return this;
    }
 
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        className = className.replace("/", ".");
        System.out.println("agent className:" + className);
        if (methodMap.containsKey(className)) {
            try {
                CtClass ctClass = ClassPool.getDefault().get(className);
                //--给对应对象的所有方法添加耗时统计
                for (String methodName : methodMap.get(className)) {
                    String cost = "\nSystem.out.println(\"   Method:"
                            + methodName
                            + " Cost:\"+(endTime-startTime)+"
                            + "\"ns\");\n";
                    CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
                    String wrappedMethodName = methodName + "$wrapped";
                    ctMethod.setName(wrappedMethodName);
                    CtMethod newMethod = CtNewMethod.copy(ctMethod, methodName, ctClass, null);
                    StringBuilder newMethodBody = new StringBuilder();
                    newMethodBody.append("{");
                    newMethodBody.append(startTime);
                    newMethodBody.append(wrappedMethodName + "($$);\n");
                    newMethodBody.append(endTime);
                    newMethodBody.append(cost);
                    newMethodBody.append("}");
                    newMethod.setBody(newMethodBody.toString());
                    ctClass.addMethod(newMethod);
                }
                return ctClass.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
 
 
#插件类
public class JavaTcAgentPlugins {
    public static void premain(String agentArgs, Instrumentation instrumentation) {
        System.out.println("JavaAgentTimeConsumer start");
        System.out.println("JavaAgentTimeConsumer params:" + agentArgs);
        instrumentation.addTransformer(new TimeConsumerTransformer());
        System.out.println("JavaAgentTimeConsumer result");
    }
 
    public static void main(String[] args) {
        //--为打包成jar作用
        System.out.println("JavaTimeConsumerAgent test...");
    }
}

6,按之前的方式打包jar


7.png

需要注意的是MANIFEST.MF需要修改为Premain-Class。


8.png

7,测试
执行java -javaagent:../examples_plugins_jar/examples-plugins.jar=whgtest -jar examples.jar


9.png

可以看到耗时统计的代码已经动态添加到原方法中了。

上一篇 下一篇

猜你喜欢

热点阅读