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. 为什么下一个十年的主战场在 Serverless?

    作者 | 不瞋 阿里云 Serverless 负责人 "唯有超越,才能让我们走下去." 这是不瞋在阿里的第十年.从 2010 年加入阿里云,不瞋参与了阿里云飞天分布式系统的研发,历 ...

  2. Docker-初见

    目录 Docker概述 Docker历史 Docker Docker的基本组成 Docker安装 使用流程 底层原理 Docker的常用命令 Portainer 可视化面板安装 镜像原理之联合文件系统 ...

  3. 【数据结构】c语言实现集合的交并差运算

    待改写:存储数据类型int-->char 重复的元素可存储 功能上不完善 #include <stdio.h> #include <stdlib.h> typedef s ...

  4. 关于 我的博客和Git-hub

    欢迎大家到我的GitHub 热烈讨论 https://github.com/ljj-19951010 由于另一个博客忘了怎么登陆了,换用此博客(仅供个人学习使用,请勿传播) 如果想看 特别详细的教程请 ...

  5. kivy Label标记文本

    from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.lang import Builder # 注意 ...

  6. 服务器端的GPU使用

    服务器端的GPU使用 查看GPU信息 查看nvidia GPU信息: # 输入指令 lspci | grep -i nvidia # 结果如下: # 04:00.0 3D controller: NV ...

  7. freemarker中使用String字符串作为模板

    在日常开发中,我们有时候需要发送短信.邮件等通知,但是这些通知的内容通常都是动态的,而且可能会发生变动,为了程序的灵活性,我们通常会将通知的内容配置在页面上,然后后台通过渲染这些模板,来获取具体的内容 ...

  8. Qt坐标转换系统的理解

    转 https://blog.csdn.net/hgcprg/article/details/53537106 今天又看了一篇对Qt坐标转换系统以及QTransform的博客,作者讲的非常透彻,链接如 ...

  9. Netty:Reactor Pattern 与 Dubbo 底层传输中的 NettyServer

    首先,我们需要了解Reactor模式的三种线程模型: 1)单线程模型 Reactor 单线程模型,指的是所有的 IO 操作都在同一个 NIO 线程上面完成,NIO 线程的职责如下: 作为 NIO 服务 ...

  10. Spring中自定义Schema扩展机制

    一.前言 Spring 为基于 XML 构建的应用提供了一种扩展机制,用于定义和配置 Bean. 它允许使用者编写自定义的 XML bean 解析器,并将解析器本身以及最终定义的 Bean 集成到 S ...