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();
                }
            }
        }
    }
    

      

  • 相关阅读:
    C++ 多线程编程
    协程简介(coroutine)
    Yanhua Digimaster 3如何使用免焊适配器重置仪表板?
    Autel OTOFIX IM1 远程/在线技术支持指南
    Xhorse奥迪免焊适配器套装功能列表+常见问题
    如何通过 DDD 构建一辆汽车
    周末复习一波Linux,Linux常用命令总结,还有语法+案例
    Dubbo 基础知识
    GIT版本控制学习博客
    C++检测和定位内存泄漏
  • 原文地址:https://www.cnblogs.com/yuanyb/p/13415815.html
Copyright © 2011-2022 走看看