zoukankan      html  css  js  c++  java
  • 使用java-agent的agentmain实现热修改

    之前的使用自定义类加载器实现热修改:https://www.cnblogs.com/yuanyb/p/12066388.html

    这两天学习了java-agent,之前对这个就有兴趣,一直想学习来着,昨天借着实习任务就学习了一下。

    附上javassist文档地址:http://www.javassist.org/tutorial/tutorial.html

    java-agent 有两种,分别是:JDK5引入的 premain,和 JDK6 引入的 agentmain,前者可以在 JVM 加载类之前拦截并修改字节码,后者可以在运行时将 agent 附加到到任意的虚拟机中来修改字节码,并且,修改后可以立马更新,不需要重新加载类,因此可以实现热修改,并且比自定义类加载器更方便。修改字节码就得靠字节码框架了,如 ASM,javassist。

    例如,给已经运行的 Java 程序的某个类的方法添加耗时监测。先打包 agent(需要配置manifest),然后执行task的main方法来启动一个虚拟机进程,最后执行Test的main方法动态修改Task类。

    agent:

    public class AgentMain {
        public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
            System.out.println("agentmain");
            inst.addTransformer(new Transformer(agentArgs), true);
            inst.retransformClasses(Task.class); // 允许修改Task类
        }
    
        private static class Transformer implements ClassFileTransformer {
            private final String targetClassName;
    
            public Transformer(String targetClassName) {
                this.targetClassName = targetClassName;
            }
    
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                className = className.replaceAll("/", ".");
                if (!className.equals(targetClassName)) {
                    return null;
                }
                System.out.println("transform: " + className);
    
                ClassPool classPool = ClassPool.getDefault();
                classPool.appendClassPath(new LoaderClassPath(loader)); // 将要修改的类的classpath加入到ClassPool中,否则找不到该类
                try {
                    CtClass ctClass = classPool.get(className);
                    for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
                        if (Modifier.isPublic(ctMethod.getModifiers()) && !ctMethod.getName().equals("main")) {
                            // 修改字节码
                            ctMethod.addLocalVariable("begin", CtClass.longType);
                            ctMethod.addLocalVariable("end", CtClass.longType);
                            ctMethod.insertBefore("begin = System.currentTimeMillis();");
                            ctMethod.insertAfter("end = System.currentTimeMillis();");
                            ctMethod.insertAfter("System.out.println("方法" + ctMethod.getName() + "耗时"+ (end - begin) +"ms");");
                        }
                    }
                    ctClass.detach();
                    return ctClass.toBytecode();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return classfileBuffer;
            }
        }
    }    
    

      

    要被热修改的类:

    public class Task implements Runnable {
        @Override
        public void run() {
            System.out.println("111");
        }
    
        public static void main(String[] args) throws Exception {
            Task task = new Task();
            while (true) {
                TimeUnit.SECONDS.sleep(3);
                task.run();
            }
        }
    }
    

      

    将代理attach到某个虚拟机进程中:

    public class Test {
        public static void main(String[] args) throws Exception {
            System.out.println("main start");
            for (VirtualMachineDescriptor descriptor : VirtualMachine.list()) {
                if (descriptor.displayName().equals("test.Task")) {
                    VirtualMachine virtualMachine = VirtualMachine.attach(descriptor.id());
                    virtualMachine.loadAgent("E:\Programming\java-agent\target\java-agent-1.0-SNAPSHOT.jar",
                            "test.Task"); // 传入agent的jar包路径,test.Task是一个String agentArgs,就像main方法的String[] args,使用户传入的,test.Task表示要热修改test.Task类
                    virtualMachine.detach();
                }
            }
        }
    }
    

      

  • 相关阅读:
    pipelinewise 学习二 创建一个简单的pipeline
    pipelinewise 学习一 docker方式安装
    Supercharging your ETL with Airflow and Singer
    ubuntu中使用 alien安装rpm包
    PipelineWise illustrates the power of Singer
    pipelinewise 基于singer 指南的的数据pipeline 工具
    关于singer elt 的几篇很不错的文章
    npkill 一个方便的npm 包清理工具
    kuma docker-compose 环境试用
    kuma 学习四 策略
  • 原文地址:https://www.cnblogs.com/yuanyb/p/13415815.html
Copyright © 2011-2022 走看看