zoukankan      html  css  js  c++  java
  • java高级-动态注入替换类Instrumentation

    介绍

    • 利用java.lang.instrument(容器类) 做动态 Instrumentation(执行容器) 是 Java SE 5 的新特性。
    • 使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。
    • 这个功能为虚拟机监控提供了支撑。

    基本用法

    1. 编写 premain 函数

    编写一个 Java 类,包含如下两个方法当中的任何一个

    public static void premain(String agentArgs, Instrumentation inst);  [1]
    public static void premain(String agentArgs); [2]
    
    其中,[1] 的优先级比 [2] 高,将会被优先执行([1] 和 [2] 同时存在时,[2] 被忽略)。
    
    agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main 函数不同的是,这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。
    
    Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这个包的核心部分,集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
    

    2. jar 文件打包

    将这个 Java 类打包成一个 jar 文件,并在其中的 manifest 属性当中加入” Premain-Class”来指定步骤 1 当中编写的那个带有 premain 的 Java 类。(可能还需要指定其他属性以开启更多功能)

    3. 运行

    用如下方式运行带有 Instrumentation 的 Java 程序:
    java -javaagent:jar 文件的位置 [= 传入 premain 的参数 ]

    案例

    1,编写正常的类

    主类

    package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
    public class TestMainInJar {
        public static void main(String[] args) {
            System.out.println(new TransClass().getNumber());
        }
    }
    

    工具类

    package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
    public class TransClass {
        public int getNumber() {
            return 1;
        }
    }
    
    
    

    2,编写代理类

    添加Premain类

    package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
    import java.lang.instrument.Instrumentation;
    import java.lang.instrument.UnmodifiableClassException;
    
    public class Premain {
        public static void premain(String agentArgs,Instrumentation inst)
                throws ClassNotFoundException,UnmodifiableClassException
        {
            System.out.println("Premain");
            inst.addTransformer(new Transformer());
        }
    }
    

    类文件转化器:
    注意:实际测试时,要去掉中文,否则javac编译报错。

    
    package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
    
    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 = "E:\Git\Java\JavaLearning\src\main\java\com\yixiu\javabase\modules\dynamic\instrumentation\demo1\classes\TransClass2.java.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();
    
                System.out.println("class length:" + String.valueOf( bytes.length ) );
    
                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 {
    
    
            System.out.println("load:" + className );
            
            // 注意,类名称是完整的路径  load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TransClass
            if(!className.contains("TransClass") ) {
                return null;
            }
    //        if (!className.equals("TransClass")) {
    //            return null;
    //        }
            return getBytesFromFile(classNumberReturns2);
    
        }
    }
    
    

    3,编译,打包

    创建目录classes,编译所有文件到classes中  
    javac -d .classes .*.java
    
    切换目录
    cd classes
    
    打包
    jar -cvf my.jar .*
    
    使用rar打开my.jar,修改MANIFEST.MF,添加
    Premain-Class: com.yixiu.javabase.modules.dynamic.instrumentation.demo1.Premain
    
    

    4,实际要动态注入替换的类

    javac编译时检查(后缀名是.java,类名和文件名相同)
    要添加的代替类,必须编译为字节码后才可以添加
    先修改类TransClass的返回值,编译后,将文件名重命名为TransClass2.java.2,和jar包放在同一个目录中

    package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
    public class TransClass {
        public int getNumber() {
            return 2;
        }
    }
    
    
    

    5,编译修改的包,执行

    mentationdemo1classes>java -javaagent:my.jar -cp my.jar  com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar
    
    输出如下:
    E:GitJavaJavaLearningsrcmainjavacomyixiujavabasemodulesdynamicinstru
    mentationdemo1classes>java -javaagent:my.jar -cp my.jar com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar
    Premain
    load:java/lang/invoke/MethodHandleImpl
    load:java/lang/invoke/MethodHandleImpl$1
    load:java/lang/invoke/MethodHandleImpl$2
    load:java/util/function/Function
    load:java/lang/invoke/MethodHandleImpl$3
    load:java/lang/invoke/MethodHandleImpl$4
    load:java/lang/ClassValue
    load:java/lang/ClassValue$Entry
    load:java/lang/ClassValue$Identity
    load:java/lang/ClassValue$Version
    load:java/lang/invoke/MemberName$Factory
    load:java/lang/invoke/MethodHandleStatics
    load:java/lang/invoke/MethodHandleStatics$1
    load:sun/misc/PostVMInitHook
    load:sun/usagetracker/UsageTrackerClient
    load:java/util/concurrent/atomic/AtomicBoolean
    load:sun/usagetracker/UsageTrackerClient$1
    load:sun/usagetracker/UsageTrackerClient$4
    load:sun/usagetracker/UsageTrackerClient$2
    load:java/lang/ProcessEnvironment
    load:java/lang/ProcessEnvironment$NameComparator
    load:java/lang/ProcessEnvironment$EntryComparator
    load:java/util/Collections$UnmodifiableMap
    load:java/lang/ProcessEnvironment$CheckedEntrySet
    load:java/util/HashMap$EntrySet
    load:java/lang/ProcessEnvironment$CheckedEntrySet$1
    load:java/util/HashMap$EntryIterator
    load:java/util/HashMap$HashIterator
    load:java/lang/ProcessEnvironment$CheckedEntry
    load:sun/usagetracker/UsageTrackerClient$3
    load:java/io/FileOutputStream$1
    load:sun/launcher/LauncherHelper
    load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TestMainInJar
    load:sun/launcher/LauncherHelper$FXHelper
    load:java/lang/Class$MethodArray
    load:java/lang/Void
    load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TransClass
    class length:309
    2
    load:java/lang/Shutdown
    load:java/lang/Shutdown$Lock
    
    
    

    参考

  • 相关阅读:
    HTML 拖放 和 地理定位
    HTML web存储
    HTML 语义元素 和 MathML元素
    Docker Swarm
    Docker Machine
    Docker Compose
    Docker 的网络模式
    数据共享与持久化
    镜像和容器的基本操作
    Docker 简介
  • 原文地址:https://www.cnblogs.com/wang7/p/10116207.html
Copyright © 2011-2022 走看看