操作 Java 字节码

栏目: Java · 发布时间: 5年前

内容简介:通常对于用 idea 的同学来说,class 文件是直接可以查看的,可以看到像 java 那样的代码。其实 class 文件是一种字节码文件,我们平时在 idea 所看到的,是 idea 自动反编译后的结果。如果把 class 文件用 sublime 打开,就会看到许多字节码,而不是 Java 代码了。像这样:Class文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在 Class 文件中,中间无任何分隔符。我们这里所说的操作 Java 字节码,就是操作修改 class 文件

通常对于用 idea 的同学来说,class 文件是直接可以查看的,可以看到像 java 那样的代码。其实 class 文件是一种字节码文件,我们平时在 idea 所看到的,是 idea 自动反编译后的结果。如果把 class 文件用 sublime 打开,就会看到许多字节码,而不是 Java 代码了。像这样:

cafe babe 0000 0034 0017 0100 1163 6e2f
6863 6873 7475 6469 6f2f 5573 6572 0700
0101 0010 6a61 7661 2f6c 616e 672f 4f62
6a65 6374 0700 0301 0004 6e61 6d65 0100
......

Class文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在 Class 文件中,中间无任何分隔符。

我们这里所说的操作 Java 字节码,就是操作修改 class 文件内容。

Why

同学们可能会有这样一个疑问,为什么要操作 Java 字节码,直接改 java 文件不是很好吗?

很多情况下是无法操作的 java 文件的,或者使用修改字节码的方式更方便:

  1. 在第三方依赖中加入一些检测数据
  2. AOP 操作,例如 Android 自动埋点统计
  3. Spring 框架的 AOP 操作使用 ASM 操作 Java 字节码

总的来说,可以更方便开发,也同时了解一些底层的原理。

Javassist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba(千叶 滋)所创建的。它已加入了开放源代码 JBoss 应用服务器项目,通过使用Javassist对字节码操作为 JBoss 实现动态”AOP”框架。

导包

compile group: 'org.javassist', name: 'javassist', version: '3.23.1-GA'

版本号可能不是最新的,想要最新的话查找 Maven 仓库获取最新的版本号即可。

类搜索路径

通过 ClassPool.getDefault() 获取的 ClassPool 使用 JVM 的类搜索路径。如果程序运行在 JBoss 或者 Tomcat 等 Web 服务器上,ClassPool 可能无法找到用户的类,因为 Web 服务器使用多个类加载器作为系统类加载器。在这种情况下,ClassPool 必须添加额外的类搜索路径。

下面的例子中,pool 代表一个 ClassPool 对象:

pool.insertClassPath(new ClassClassPath(this.getClass()));

上面的语句将 this 指向的类添加到 pool 的类加载路径中。你可以使用任意 Class 对象来代替 this.getClass(),从而将 Class 对象添加到类加载路径中。传参支持 ClassPath、URLClassPath、ByteArrayClassPath 类型。

编辑

// 创建 User 类
CtClass ctClass = classPool.makeClass("cn.hchstudio.User");
// 获取 String 类
CtClass CtString = classPool.get("java.lang.String");

通过 makeClass 和 get 方法可以分别创建、获取 CtClass,进而操作类。

CtField name = new CtField(CtString, "name", ctClass);
name.setModifiers(Modifier.PRIVATE);
ctClass.addField(name);

上面的语句是创建一个变量,new CtField 中分别传入类型、名称、ctClass。setModifiers 设置变量修饰符;addField 表示把变量加入到这个类中。

CtMethod setSex = CtMethod.make("public void setSex(java.lang.String sex){" +
                    "this.sex = sex;" +
                    "}", ctClass);
ctClass.addMethod(setSex);

Javassist 有一个简单除暴的新增方法方式,就是直接把要写的 java 代码变为字符串,之后 Javassist 便可自动完成代码校验,转为字节码的过程。

对于已存在的方法,可以使用 insertBefore、insertAfter 方法插入到方法函数之后或之后。

ctMethod.insertBefore("System.out.println(\"lalala\");");
ctMethod.insertAfter("System.out.println(\"lalala\");");

一个栗子

举一个栗子,这里通过 Javassist 生成一个 User 类,其中包括 name、sex 属性,并有其 set、get 方法。并且输出到 ./out/production/classes 目录下。

ClassPool classPool = ClassPool.getDefault();

try {
    CtClass ctClass = classPool.makeClass("cn.hchstudio.User");

    CtClass CtString = classPool.get("java.lang.String");

    CtField name = new CtField(CtString, "name", ctClass);
    name.setModifiers(Modifier.PRIVATE);
    ctClass.addField(name);
    CtField sex = new CtField(CtString, "sex", ctClass);
    sex.setModifiers(Modifier.PRIVATE);
    ctClass.addField(sex);

    CtMethod setName = new CtMethod(CtClass.voidType, "setName",
            new CtClass[]{CtString}, ctClass);
    setName.setModifiers(Modifier.PUBLIC);
    setName.setBody("name = $1;");
    ctClass.addMethod(setName);
    CtMethod getName = new CtMethod(CtString, "getName",
            new CtClass[]{}, ctClass);
    getName.setModifiers(Modifier.PUBLIC);
    getName.setBody("return name;");
    ctClass.addMethod(getName);
    CtMethod setSex = CtMethod.make("public void setSex(java.lang.String sex){" +
            "this.sex = sex;" +
            "}", ctClass);
    ctClass.addMethod(setSex);
    CtMethod getSex = new CtMethod(CtString, "getSex",
            new CtClass[]{}, ctClass);
    getSex.setModifiers(Modifier.PUBLIC);
    getSex.setBody("return sex;");
    ctClass.addMethod(getSex);

    ctClass.writeFile("./out/production/classes");
} catch (Exception e) {
    System.out.println(e.toString());
    e.printStackTrace();
}

ASM

ASM 也是一个操作 Java 字节码的框架,相比于 Javassist,它更加底层、轻量级、速度也快,不过在编写代码的时候可能容易出错,它需要我们直接写 Java 字节码。

Java jdk 自带了 ASM 的依赖,在 rt.jar!/jdk/internal/org/objectweb/asm 下。

Android 环境下则需要自己导入依赖,因为 Android 去掉了 rt.jar!/jdk 包。

编辑

ASM 编辑代码则比较复杂,需要对汇编有一定了解的同学才可以。

通常的方式是我们需要用 java 写出一个想要自动生成的类,然后查看他的 class 字节码

mv = cw.visitMethod(ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, "cn/hchstudio/User", "name", "Ljava/lang/String;");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();

这里改出一段示例代码,意思为新建一个 setName 方法,并给 name 属性赋值。

操作 Java 字节码


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

《生活大爆炸》之科学揭秘

《生活大爆炸》之科学揭秘

乔治·毕姆 / 韩准、徐漪、江业华、叶夜 / 世界图书出版公司 / 2012-12 / 49.00元

《 之科学揭秘:GEEK探索频道》对流行美剧《生活大爆炸》进行“深度解密”,重点在解读剧中涉及的流行文化及科学元素。正如我们所知,《生活大爆炸》是一部“技术含量很高”的肥皂剧。不光是普通观众,科学家也爱《生活大爆炸》。《 之科学揭秘:GEEK探索频道》中,科学家详尽为你解释了电视剧中出现的科学道理和典故。包括谢尔顿的高深弦理论、霍华德的花生过敏是怎么回事、如果你和谢尔顿的妈妈有同样的信仰该如何看待......一起来看看 《《生活大爆炸》之科学揭秘》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具