Tips: ASM使用访问者模式,学会访问者模式再看ASM更加清晰

ClassReader

用于读取字节码,父类是Object

主要作用:

  1. 分析字节码里各部分内容,如版本、字段等等
  2. 配合其他Visitor使用

主要使用的方法

public void accept(ClassVisitor classVisitor, int parsingOptions) {
this.accept(classVisitor, new Attribute[0], parsingOptions);
} // 第一个参数是访问者,第二个参数用于跳过读取字节码的一些信息,常用ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES,跳过没必要的调试和帧信息以缩小大小

如何修改已存在的字节码

通过继承ClassVisitor,重写visitxxx方法,在方法中对访问到的数据进行操作,然后传给下一个ClassVisitor的子类处理(如果有的话)。经过一个或多个ClassVisitor的访问链处理后,传到ClassWriter手里,由ClassWriter生成最终的字节码结果

ClassVisitor方法通过构造函数可以传递下一个Visitor进去,如同链表一样,一个个的形成访问链,最后链接到ClassWriter这个特殊的ClassVisitor里去形成结果

实例

被修改的类

package example;

public class TestClass01 {
public int a;
public int b; public TestClass01() {
System.out.println("init!!");
}
}

ASM代码

package example.modify;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter; import java.io.IOException; import example.modify.clsvisitor.*;
import org.objectweb.asm.Opcodes;
import utils.FileUtils; public class Test01 { public static void main(String[] args) throws IOException {
String path = FileUtils.getFilePath("example/sample/TestClass01.class");
FileUtils.writeBytes(path, modifyClass());
} public static byte[] modifyClass() {
ClassReader cr = null;
try {
cr = new ClassReader("example.TestClass01");
} catch (IOException e) {
e.printStackTrace();
}
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassVisitor cv = new TestClass01Visitor(Opcodes.ASM9, cw);
cr.accept(cv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return cw.toByteArray();
}
}
package example.modify.clsvisitor;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes; public class TestClass01Visitor extends ClassVisitor {
private String addFieldN = "content";
private boolean flag1 = true;
private String addStaticFieldN = "myID";
private int addStaticFieldV = 100;
private boolean flag2 = true;
private String delFieldN = "b"; public TestClass01Visitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
} /**
* 把java版本改成1.6 全限定名改成sample包下,避免两个同样的类冲突
* @param version
* @param access
* @param name
* @param signature
* @param superName
* @param interfaces
*/
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(Opcodes.V1_6, access, "example/sample/TestClass01", signature, superName, interfaces);
} /**
* 添加两个成员删一个成员
* @param access
* @param name
* @param descriptor
* @param signature
* @param value
* @return
*/
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if (name.equals(delFieldN)) return null;
if (name.equals(addFieldN)) flag1 = false; // 存在就不添加
if (name.equals(addStaticFieldN)) flag2 = false;
return super.visitField(access, name, descriptor, signature, value);
} @Override
public void visitEnd() {
super.visitField(
Opcodes.ACC_PUBLIC,
this.addFieldN,
"Ljava/lang/String;",
null,
""
);
super.visitField(
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
this.addStaticFieldN,
"I",
null,
this.addStaticFieldV
);
super.visitEnd();
}
}

生成的字节码结果(idea反编译后)

Java-ASM框架学习-修改类的字节码的更多相关文章

  1. 从Java进程里dump出类的字节码文件

    想要查看一些被增强过的类的字节码,或者一些AOP框架的生成类,就需要dump出运行时的Java进程里的字节码. 从运行的java进程里dump出运行中的类的class文件的方法: 用agent att ...

  2. 浅谈Java反射机制 之 获取类的字节码文件 Class.forName("全路径名") 、getClass()、class

    另一个篇:获取 类 的 方法 和 属性(包括构造函数) 先贴上Java反射机制的概念: AVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: 对于任意一个对象,都能够调用它 ...

  3. Java-ASM框架学习-从零构建类的字节码

    Tips: ASM使用访问者模式,学会访问者模式再看ASM更加清晰 什么是ASM ASM是一个操作Java字节码的类库 学习这个类库之前,希望大家对Java 基本IO和字节码有一定的了解. 高版本的A ...

  4. Java集合框架(常用类) JCF

    Java集合框架(常用类) JCF 为了实现某一目的或功能而预先设计好一系列封装好的具有继承关系或实现关系类的接口: 集合的由来: 特点:元素类型可以不同,集合长度可变,空间不固定: 管理集合类和接口 ...

  5. Java最重要的21个技术点和知识点之JAVA集合框架、异常类、IO

    (三)Java最重要的21个技术点和知识点之JAVA集合框架.异常类.IO  写这篇文章的目的是想总结一下自己这么多年JAVA培训的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能分享 ...

  6. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  7. Java虚拟机JVM学习04 类的初始化

    Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...

  8. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  9. JVM学习笔记——类加载和字节码技术篇

    JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...

随机推荐

  1. 精准容量、秒级弹性,压测工具 + SAE 方案如何完美突破传统大促难关?

    作者 | 代序 阿里云云原生技术团队 本文整理自<Serverless 技术公开课>,"Serverless"公众号后台回复"入门",即可获取系列文 ...

  2. 解决VM 与 Device/Credential Guard 不兼容

    通过命令关闭Hyper-V(控制面板关闭Hyper-V起不到决定性作用,要彻底关闭Hyper-V) 以管理员身份运行Windows Powershell (管理员)(Windows键+X) 运行下面命 ...

  3. NX Open 图层说

    我也是偶然发现的,在一次调试下,竟然会报警. 所以我写了测试代码,进行测试:结果如下 纳尼???还有271层?还能设置大于256层?NX open可以的.

  4. 最详细的Android SDK下载安装及配置教程-------全文均为引用

    <https://www.cnblogs.com/gufengchen/p/11038029.html>

  5. MySQL:基础语法-4

    MySQL:基础语法-4 记录一下 MySQL 基础的一些语法,便于查询,该部分内容主要是参考:bilibili 上 黑马程序员 的课程而做的笔记,由于时间有点久了,课程地址忘记了 上文MySQL:基 ...

  6. spring源码分析(二)- 容器基础

    1.基本用法 用过Spring的都知道,bean是Spring中最基础也是最核心的.首先看一个简单的例子. 一个类和一个配置文件 package bean; public class MyBean { ...

  7. 关于string转换为wstring问题

    方法一:需要调用windows的api函数进行转换,在vs2017上试验转换成功 #ifdef _MSC_VER #include <Windows.h> #endif // _MSC_V ...

  8. 零基础入门C语言超详细的字符串详解

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...

  9. Linux过来人帮你理清学习思路

    很多同学接触linux不多,对linux平台的开发更是一无所知. 而现在的趋势越来越表明,作为一个优秀的软件开发人员,或计算机it行业从业人员,="" 掌握linux是一种很重要的 ...

  10. 洛谷 P2221 [HAOI2012]高速公路

    链接: P2221 题意: 有 \(n(1\leq n\leq 10^5)\) 个点,从第 \(i(1\leq i< n)\) 个点向第 \(i+1\) 个点连有边.最初所有边长 \(v_i\) ...