背景

想调用ASM API (用于字节码处理的开源API)对字节码进行处理,目标是实现对java程序运行时各种对象的动态跟踪,并进一步分析各个对象之间的关系(研究前提是目前的UML锁阐释的whole-part relation 是比较混乱的)。由于ASM相关内容又可以延伸很远,在此文中略过。

在完成了能对字节码进行处理的ASM调用以后,需要考虑如何将这些功能与正常的java程序整合到一起。

首先,我考虑到了自定义ClassLoader的方法(具体内容见另一篇博客《ClassLoader编程实践》),即在程序的main入口处,首先加载自定义的classloader,然后通过reflect技术使用这个classloader加载并调用测试程序(就是被跟踪的程序,姑且称之为测试程序)的入口类。

这个方法一个很大的限制,在于每次都必须先找到测试程序的入口类,而对于有的封装成jar的程序集合,这一点相对比较难控制。

于是,有了这里介绍的方法:通过 java.lang.instrument 实现的java agent对象操作字节码,也就试所谓的AOP(面向方面编程)

中文介绍参加IBM developworks上的文档:《Java SE 6 新特性: Instrumentation
新功能
》。内有详细解释。其中提到的BCEL即Byte Code Engineering Library,是apache的开源项目,挺复杂,而ASM库有相对详尽的说明文档。这也是我选择了ASM实现的原因。

博主的程序里,其实很多上文提到的java6新特性都没有使用到:虚拟机启动后的动态instrument ,本地方法的instrument 都没有使用到。这里仅就源代码中的程序简单介绍instrument的基本功能和用法。

程序中,除了ASMAgent 以外的所有类都是调用ASM API 实现对 测试程序中 各个对象的construct 、 方法调用 , field赋值等操作行为的记录(其中对Collection子类的处理着实费了一番心血= =,字节码操作很细节,容易出错。以后再写ASM方面的博文叙述)。

在此,我们将ASM API调用部分抽象成一个整体的接口好了,就是如下几句代码(在ASMAgent.java中):

  1. ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
  2. ASMClassAdapter mca = new ASMClassAdapter(cw);
  3. ClassReader cr = new ClassReader(classfileBuffer);
  4. cr.accept(mca, 0);
  5. retVal = cw.toByteArray();

其中classfileBuffer是类文件加载时的原始的字节码,retVal则是经过处理后的字节码。

============================正文分割===========================

原理

JVMTI(Java Virtual Machine Tool Interface)是一套本地编程接口集合,它提供了一套”代理”程序机制,可以支持第三方工具程序以代理的方式连接和访问 JVM,并利用 JVMTI 提供的丰富的编程接口,完成很多跟 JVM 相关的功能。关于 JVMTI 的详细信息,请参考 Java SE 6 文档(请参见 参考资源)当中的介绍。

java.lang.instrument 包的实现,也就是基于这种机制的:在 Instrumentation 的实现当中,存在一个 JVMTI 的代理程序,通过调用 JVMTI 当中 Java 类相关的函数来完成 Java 类的动态操作。

Instrumentation 的最大作用,就是类定义动态改变和操作。在 Java SE 5 及其后续版本当中,开发者可以在一个普通 Java 程序(带有 main 函数的 Java 类)运行时,通过 – javaagent参数指定一个特定的 jar 文件(包含 Instrumentation 代理)来启动 Instrumentation 的代理程序。

步骤

1.编写java代理类。

这个类中,premain方法是关键,对比于一般的入口main一样,这里的premain是在main之前执行的。它会告诉JVM如何处理加载上来的java字节码。如下例:

  1. public static void premain(String agentArgs, Instrumentation inst) {
  2. Trace.BeginTrace(); // it's important for trace files
  3. inst.addTransformer(new ASMAgent());
  4. }

值得注意的是,addTransformer实现了对字节码处理的方法的回调。

  1. inst.addTransformer(new ASMAgent());

类ASMAgent包含着实现对java字节码处理的方法:transform()。它来自于ClassFileTransformer接口。为了方便,博主这里将对ClassFileTransformer接口的实现跟ASMAgent类放在了一起。

  1. public byte[] transform(ClassLoader loader, String className,Class<?> classBeingRedefined,
  2. ProtectionDomain protectionDomain,byte[] classfileBuffer)
  3. throws IllegalClassFormatException {
  4. byte[] retVal = null;
  5. if(isInstrumentable(className)){
  6. ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
  7. ASMClassAdapter mca = new ASMClassAdapter(cw);
  8. ClassReader cr = new ClassReader(classfileBuffer);
  9. cr.accept(mca, 0);
  10. retVal = cw.toByteArray();
  11. }else{
  12. retVal = classfileBuffer ;
  13. }
  14. return retVal;
  15. }

2.打包代理类。

只有合理打包并在manifest文件中记录下相应的键值对之后,才能正常执行premain的内容。

manifest文件中需要添加的键值对是:

  1. Premain-Class : biaobiaoqi.asm.ASMAgent

jar打包和manifest的修改方法见另一篇博文《JAR文件操作基础(Using
JAR Files: The Basics)

另外,如果对字节码的处理有应用到了其他的类,需要在manifest中增加路径。键值对为:

  1. Class-Path: asm-3.0.jar

3.执行

执行测试程序时,添加“-javaagent:代理类的jar[=传入premain的参数]”选项。

比如,对于博主的程序,就是

java -javaagent:ASMInstrument.jar   -jar XXXX.jar  xxxx

其中ASMInstrument.jar 是第二步中打包的程序,  XXX.jar是需要测试的程序, xxx是XXX.jar 执行时可能的命令行参数。

如果只是执行某.class文件中的类,我们假设是在当前目录下的一个XXXX类,则是:

java -javaagent:ASMInstrument.jar   -cp ./  XXXX xxx

其中xxx是可能的命令行参数。

源码下载

编码期间遇到很多问题,比如在instrument方法中的classlodaer的一些判定操作。希望跟大家多交流~求勿喷。

java.lang.instrument 中的premain 实现类的个性化加载(附源代码)的更多相关文章

  1. java.lang和java.lang.annotation中实现Annotation的类小结

    加了注解,等于打上了某种标记,没加,则等于没有某种标记,以后,其他程序可以用反射来了解你的类上面有无何种标记,看你有什么标记,就去干相应的事.标记可以加在类,方法,字段,包上,方法的参数上. (1)  ...

  2. int是java.lang包中可用的类的名称

    int是java.lang包中可用的类的名称(x) int为基本数据类型,不是类

  3. java.lang.instrument使用

    Java在1.5引入java.lang.instrument,你可以由此实现一个Javaagent,通过此agent来修改类的字节码即改变一个类. 程序启动之时启动代理(pre-main) 通过jav ...

  4. J2SE 1.6 特性:java.lang.instrument

    1. import java.lang.instrument.Instrumentation; public class ObjectSizeFetcher { private static Inst ...

  5. java.lang.instrument: 一个Java对象占用多少字节?

    一.对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针)   1. Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode).GC分代年 ...

  6. java.lang.instrument.Instrumentation

    java.lang.instrument.Instrumentation 看完文档之后,我们发现这么两个接口:redefineClasses和retransformClasses.一个是重新定义cla ...

  7. JDK框架简析--java.lang包中的基础类库、基础数据类型

    题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含 ...

  8. java动态编译类文件并加载到内存中

    如果你想在动态编译并加载了class后,能够用hibernate的数据访问接口以面向对象的方式来操作该class类,请参考这篇博文-http://www.cnblogs.com/anai/p/4270 ...

  9. JAVA类的静态加载和动态加载以及NoClassDefFoundError和ClassNotFoundException

    我们都知道Java初始化一个类的时候可以用new 操作符来初始化, 也可通过Class.forName()的方式来得到一个Class类型的实例,然后通过这个Class类型的实例的newInstance ...

随机推荐

  1. Java常用数据结构之Set之TreeSet

    前言 上篇文章我们分析了HashSet,它是基于HashMap实现的,那TreeSet会是怎么实现的呢?没错!和大家想的一样,它是基于TreeMap实现的.所以,TreeSet的源码也很简单,主要还是 ...

  2. PHPCMS V9 全站通用日期时间标签

    用PHPCMS V9 建站时,经常会用到时间标签,它是通用标签调用-日期时间格式化,适用全站. 1.日期时间格式化显示: a\标准型:{date('Y-m-d H:i:s', $rs['inputti ...

  3. 【11-13】A股主要指数的市盈率(PE)估值高度

    全指材料(SH000987) - 2018-11-13日,当前值:12.4646,平均值:30.54,中位数:26.09865,当前 接近历史新低.全指材料(SH000987)的历史市盈率PE详情 内 ...

  4. redis配置文件相关

    1. 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes daemonize no 2. 当Redis在后台运行时,Redis默认会把pid写入/var/run/red ...

  5. [Bayesian] “我是bayesian我怕谁”系列 - Naive Bayes with Prior

    先明确一些潜规则: 机器学习是个collection or set of models,一切实践性强的模型都会被归纳到这个领域,没有严格的定义,’有用‘可能就是唯一的共性. 机器学习大概分为三个领域: ...

  6. 从Python学习中得到的一点启发 - Java逆向索引ArrayList

    看了几天Python,感觉记忆力不行了,很多东西记不住了.但是终归是得到了一点知识:重写一个ArrayList,允许从负值的索引得到指定的项.然后写一个得到斐波拉契数组的方法,这种方法要比递归调用的方 ...

  7. logstash结合rsyslog,收集系统日志

    rsyslog是日志收集工具.如今非常多Linux都自带rsyslog,用其替换掉syslog.怎样安装rsyslog就不讲了.大概讲下原理.然后讲logstash的相关配置. rsyslog本身有一 ...

  8. Git Step by Step – (1) Git 简介

    由于工作的需要,代码版本控制工具要从Perforce换成Git.说实话,刚开始真的很不适应,要从一个可以很好的支持用户界面的工具转到一个命令行工具,而且Git中有几百个命令,一下子就傻眼了. 但是经过 ...

  9. PHP从数组中找到指定元素的位置

    群里有人问,有个数组五个元素 分为1到5  现在要求 循环找出3元素的索引,怎么做性能才是最高. 我不知道哪个性能最高,但是我想提出可以用多种方式进行查找,然后进行比较选择. 我想,最简单最基础的 应 ...

  10. window下JBoss7 安装部署

    0x01 下载安装 1.下载地址: http://www.jboss.org/jbossas/downloads 2.解压缩:选择一个安装目录解压 jboss-as-7.1.1.Final.zip 3 ...