介绍

  • 利用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,编译修改的包,执行

mentation\demo1\classes>java -javaagent:my.jar -cp my.jar  com.yixiu.javabase.modules.dynamic.instrumentation.demo1.TestMainInJar

输出如下:
E:\Git\Java\JavaLearning\src\main\java\com\yixiu\javabase\modules\dynamic\instru
mentation\demo1\classes>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

参考

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. Linux中more和less命令用法

    一.more命令 more功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上. more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 ...

  2. 基于MVC的网站和在线教育系统

    最近老表说要创业,想要做一个网站做宣传,还想要一个在线教育系统. 学习了一部分 Java,  决定用.Net MVC做官网或直接做成静态HTML网站,主要是因为.Net MVC 技术简单,效率高,需求 ...

  3. c# 扩展方法初见理解

    个人理解扩展方法是对某些类在不改变源码的基础上添加其他的方法.扩展方法必须是在非泛型的静态类里定义,且第一个参数是要使用this 指定需要扩展的类型. class Program { static v ...

  4. keil常用配置设置

    1.设置tab键为空格4个进入Configuration->Editor,设置如下: 2.代码自动补齐进入Configuration->Editor,设置如下: 3.使用快捷键实现批量注释 ...

  5. Spring笔记01_下载_概述_监听器

    目录 Spring笔记01 1.Spring介绍 1.1 Spring概述 1.2 Spring好处 1.3 Spring结构体系 1.4 在项目中的架构 1.5 程序的耦合和解耦 2. Spring ...

  6. 多线程(3)ThreadPool

    使用Thread类已经可以创建并启动线程了,但是随着开启的线程越来越多,线程的创建和终止都需要手动操作,非常繁琐,另一个问题是,开启更多新的线程但是没有用的线程没有及时得到终止的时候,会占用越来越多的 ...

  7. 用node.js express设置路径后 子路径下的页面访问静态资源路径出问题

    在routes/news_mian.js 设置了访问news_main.html 的路径 '/',通知设置一个访问news-page.html的子路径'/newspage'子路径.但是在访问loacl ...

  8. DVWA 黑客攻防演练(七)Weak Session IDs

    用户访问服务器的时候,一般服务器都会分配一个身份证 session id 给用户,用于标识.用户拿到 session id 后就会保存到 cookies 上,之后只要拿着 cookies 再访问服务器 ...

  9. 安装MySQL8.0 遇到的3个小错误

    过去公司都是用的5.7 系列的MySQL,随着8.0的发版,也想试着升级一下.遇到了两个小错误,记录在此. 路径设置: 安装包路径:/data/mysql80/ 数据路径: /data/mysql/ ...

  10. C#-委托delegate

    目录 委托的定义 委托的声明 委托的实例 委托的注意细节 泛型委托(详见<精通C#>--10.4泛型委托) 1.Action<>委托 3.Func<>委托 附录 委 ...