介绍

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

基本用法

1. 编写 premain 函数

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

  1. public static void premain(String agentArgs, Instrumentation inst); [1]
  2. public static void premain(String agentArgs); [2]
  3. 其中,[1] 的优先级比 [2] 高,将会被优先执行([1] [2] 同时存在时,[2] 被忽略)。
  4. agentArgs premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main 函数不同的是,这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。
  5. 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,编写正常的类

主类

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

工具类

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

2,编写代理类

添加Premain类

  1. package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
  2. import java.lang.instrument.Instrumentation;
  3. import java.lang.instrument.UnmodifiableClassException;
  4. public class Premain {
  5. public static void premain(String agentArgs,Instrumentation inst)
  6. throws ClassNotFoundException,UnmodifiableClassException
  7. {
  8. System.out.println("Premain");
  9. inst.addTransformer(new Transformer());
  10. }
  11. }

类文件转化器:

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


  1. package com.yixiu.javabase.modules.dynamic.instrumentation.demo1;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.lang.instrument.ClassFileTransformer;
  7. import java.lang.instrument.IllegalClassFormatException;
  8. import java.security.ProtectionDomain;
  9. class Transformer implements ClassFileTransformer {
  10. // 注意是完整路径
  11. public static final String classNumberReturns2 = "E:\\Git\\Java\\JavaLearning\\src\\main\\java\\com\\yixiu\\javabase\\modules\\dynamic\\instrumentation\\demo1\\classes\\TransClass2.java.2";
  12. public static byte[] getBytesFromFile(String fileName) {
  13. try {
  14. // precondition
  15. File file = new File(fileName);
  16. InputStream is = new FileInputStream(file);
  17. long length = file.length();
  18. byte[] bytes = new byte[(int) length];
  19. // Read in the bytes
  20. int offset = 0;
  21. int numRead = 0;
  22. while (offset <bytes.length
  23. && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
  24. offset += numRead;
  25. }
  26. if (offset < bytes.length) {
  27. throw new IOException("Could not completely read file "
  28. + file.getName());
  29. }
  30. is.close();
  31. System.out.println("class length:" + String.valueOf( bytes.length ) );
  32. return bytes;
  33. } catch (Exception e) {
  34. System.out.println("error occurs in _ClassTransformer!"
  35. + e.getClass().getName());
  36. return null;
  37. }
  38. }
  39. public byte[] transform(ClassLoader l, String className, Class<?> c,
  40. ProtectionDomain pd, byte[] b) throws IllegalClassFormatException {
  41. System.out.println("load:" + className );
  42. // 注意,类名称是完整的路径 load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TransClass
  43. if(!className.contains("TransClass") ) {
  44. return null;
  45. }
  46. // if (!className.equals("TransClass")) {
  47. // return null;
  48. // }
  49. return getBytesFromFile(classNumberReturns2);
  50. }
  51. }

3,编译,打包

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

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

javac编译时检查(后缀名是.java,类名和文件名相同)

要添加的代替类,必须编译为字节码后才可以添加

先修改类TransClass的返回值,编译后,将文件名重命名为TransClass2.java.2,和jar包放在同一个目录中

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

5,编译修改的包,执行

  1. mentation\demo1\classes>java -javaagent:my.jar -cp my.jar com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar
  2. 输出如下:
  3. E:\Git\Java\JavaLearning\src\main\java\com\yixiu\javabase\modules\dynamic\instru
  4. mentation\demo1\classes>java -javaagent:my.jar -cp my.jar com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar
  5. Premain
  6. load:java/lang/invoke/MethodHandleImpl
  7. load:java/lang/invoke/MethodHandleImpl$1
  8. load:java/lang/invoke/MethodHandleImpl$2
  9. load:java/util/function/Function
  10. load:java/lang/invoke/MethodHandleImpl$3
  11. load:java/lang/invoke/MethodHandleImpl$4
  12. load:java/lang/ClassValue
  13. load:java/lang/ClassValue$Entry
  14. load:java/lang/ClassValue$Identity
  15. load:java/lang/ClassValue$Version
  16. load:java/lang/invoke/MemberName$Factory
  17. load:java/lang/invoke/MethodHandleStatics
  18. load:java/lang/invoke/MethodHandleStatics$1
  19. load:sun/misc/PostVMInitHook
  20. load:sun/usagetracker/UsageTrackerClient
  21. load:java/util/concurrent/atomic/AtomicBoolean
  22. load:sun/usagetracker/UsageTrackerClient$1
  23. load:sun/usagetracker/UsageTrackerClient$4
  24. load:sun/usagetracker/UsageTrackerClient$2
  25. load:java/lang/ProcessEnvironment
  26. load:java/lang/ProcessEnvironment$NameComparator
  27. load:java/lang/ProcessEnvironment$EntryComparator
  28. load:java/util/Collections$UnmodifiableMap
  29. load:java/lang/ProcessEnvironment$CheckedEntrySet
  30. load:java/util/HashMap$EntrySet
  31. load:java/lang/ProcessEnvironment$CheckedEntrySet$1
  32. load:java/util/HashMap$EntryIterator
  33. load:java/util/HashMap$HashIterator
  34. load:java/lang/ProcessEnvironment$CheckedEntry
  35. load:sun/usagetracker/UsageTrackerClient$3
  36. load:java/io/FileOutputStream$1
  37. load:sun/launcher/LauncherHelper
  38. load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TestMainInJar
  39. load:sun/launcher/LauncherHelper$FXHelper
  40. load:java/lang/Class$MethodArray
  41. load:java/lang/Void
  42. load:com/yixiu/javabase/modules/dynamic/instrumentation/demo1/TransClass
  43. class length:309
  44. 2
  45. load:java/lang/Shutdown
  46. load:java/lang/Shutdown$Lock

参考

java高级-动态注入替换类Instrumentation的更多相关文章

  1. java反射动态加载类Class.forName();

    1,所有的new出来的对象都是静态加载的,在程序编译的时候就会进行加载.而使用反射机制Class.forName是动态加载的,在运行时刻进行加载. 例子:直接上两个例子 public class Ca ...

  2. Java 扫描实现 Ioc 动态注入,过滤器根据访问url调用自定义注解标记的类及其方法

    扫描实现 Ioc 动态注入 参考: http://www.private-blog.com/2017/11/16/java-%e6%89%ab%e6%8f%8f%e5%ae%9e%e7%8e%b0-i ...

  3. 不修改源代码,动态注入Java代码的方法(转)

    转自:https://blog.csdn.net/hiphoon_sun/article/details/38707927 有时,我们需要在不修改源代码的前提下往一个第三方的JAVA程序里注入自己的代 ...

  4. java reflect 初始学习 动态加载类

    首先要理解Class类: 在java 的反射中,Class.forName("com.lilin.Office") 使用类的全名,这样获取,不仅仅表示了类的类类型,同时还代表着类的 ...

  5. Java中动态代理技术生成的类与原始类的区别 (转)

    用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...

  6. Java中动态代理技术生成的类与原始类的区别

    用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...

  7. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

  8. Java中动态代理技术生成的类与原始类的区别 (good)

    用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...

  9. java动态加载类和静态加载类笔记

    JAVA中的静态加载类是编译时刻加载类  动态加载类指的是运行时刻加载类 二者有什么区别呢 举一个例子  现在我创建了一个类  实现的功能假设为通过传入的参数调用具体的类和方法 class offic ...

随机推荐

  1. 【转】ASP.NET MVC实现权限控制

    这篇分享一下 ASP.NET MVC权限控制.也就是说某一用户登录之后,某一个用户是否有权限访问Controller,Action(操作),视图等 想实现这些功能,需要在数据库创建好几个表:[User ...

  2. WebLogic及其他

    如何给WebLogic指定大小的内存? 在启动WebLogic的脚本中(位于所在Domian对应服务器目录下的startServerName),增加set MEM_ARGS= -Xms32m -Xmx ...

  3. JSON字符串反序列化成对象_部分属性值反序列化失败

    简介:本人在开发webapi接口时遇到了:一个复杂的Json字符串在反序列化为对象时报,无法发序列化其中的一个属性对象? 使用方法: InternalRecommendRequestFormModel ...

  4. 学JAVA第七天,循环深入了解

    因为星期五放假,所以今天补回. 上次已经解释过循环了,现在我们来进一步了解. 例如for循环:for( int i=0 : i<10 : i++ ){需要循环的内容},这样就会循环10次了 如果 ...

  5. Python 100例(001)

    #!/usr/bin/env python # coding:utf-8 '''有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少?''' n = 1 num = [4, ...

  6. JS 无限长form表单提交

    1 简介 开发时候,总会遇到根据后台传的变量{组件数}来动态渲染组件的情况,比如后台传命令要绑定10个父子关系,则前台展开十个input组件,后台决定绑定5个福字关系,则前台展开5个input组件.再 ...

  7. css隐藏滚动条

    xhtml中隐藏滚动条在用ie6浏览有框架的xhtml页面的时候,默认会水平和垂直滚动条会一起出现,这是ie6的一个bug,在firefox上是正常的,出现的原因是其对XHTML 1.0 transi ...

  8. 震惊!Vector两行代码求逆序对,六行代码过普通平衡树

    Vector两行代码求逆序对 背景:济南集训Day7上午T2,出了一道逆序对的裸题,SB的我没看出是逆序对来,于是现场推了一个很刁钻的求逆序对的方法 首先我们想一下冒泡排序的过程,我们不难发现,对于每 ...

  9. ubuntu16.04 部署配置LVS主从

    实验环境---ubuntu16.04 四台机器:10.211.55.13—55.16 具体实验环境配置如下: 10.211.55.102  LVS_VIP 10.211.55.13  LVS_MAST ...

  10. 【腾讯云服务器】基于centos7搭建ftp服务器(vsftpd)

    该博客分为三部分设置,1.ftp服务器搭建.2.防火墙设置  3.腾讯云安全组 一.ftp服务器搭建 1.1  安装vsftpd yum install vsftpd -y 1.2 启动vsftpd服 ...