ASM插桩——动态添加字段并生成get set 方法

news2024/11/24 0:03:29

1 首先创建一个实体类Student. 代码如下

package com.org.xcyz.asm;
 
public class Student {
    private int id;
    private String name;
    private boolean sex;
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public boolean isSex() {
        return sex;
    }
 
    public void setSex(boolean sex) {
        this.sex = sex;
    }
}

2 然后准备添加字段age和setAge以及getAge方法

package com.org.xcyz.asm.trans;
 
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
 
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
 
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
 
public class AddVisitorTransformer1 {
    public static void main(String[] args) throws IOException {
        String className = "com.org.xcyz.asm.Student";
        String classJvmName = className.replace('.', '/');
        ClassReader cr = new ClassReader(className);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
        ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9,cw) {
            boolean hasFiled = false;
            @Override
            public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
                if("age".equals(name) && "I".equals(descriptor))hasFiled=true;
                return super.visitField(access, name, descriptor, signature, value);
            }
 
            @Override
            public void visitEnd() {
                if (!hasFiled) {
                    FieldVisitor fieldVisitor = cv.visitField(ACC_PRIVATE, "age", "I", null, null);
                    if (fieldVisitor != null) {
                        fieldVisitor.visitEnd();
                    }
                    MethodVisitor setAge = cv.visitMethod(ACC_PUBLIC, "setAge", "(I)V", null, null);
                    if (setAge != null) {
                        setAge.visitCode();
                        setAge.visitVarInsn(ALOAD,0);
                        setAge.visitVarInsn(ILOAD,1);
                        setAge.visitFieldInsn(PUTFIELD,classJvmName,"age","I");
                        setAge.visitInsn(RETURN);
                        setAge.visitMaxs(0,0);
                        setAge.visitEnd();
                    }
                    MethodVisitor getAge = cv.visitMethod(ACC_PUBLIC, "getAge", "()I", null, null);
                    if (getAge != null) {
                        getAge.visitCode();
                        getAge.visitVarInsn(ALOAD,0);
                        getAge.visitFieldInsn(GETFIELD,classJvmName,"age","I");
                        getAge.visitInsn(IRETURN);
                        getAge.visitMaxs(0,0);
                        getAge.visitEnd();
                    }
                }
                super.visitEnd();
            }
        };
        cr.accept(classVisitor,ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
        byte[] bytes = cw.toByteArray();
        Files.write(Paths.get("target/classes/"+classJvmName+".class"),bytes,TRUNCATE_EXISTING,CREATE);
    }
}

3. 动态添加字段的时机

3.1 visit方法中

override fun visit(
        version: Int,
        access: Int,
        name: String?,
        signature: String?,
        superName: String?,
        interfaces: Array<out String>?
    ) {
        println("visit access $access className $className name $name  signature $signature")

        val numFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "age", "I", null, 25)
        numFiledVisitor.visitEnd()
        val strFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "str", "Ljava/lang/String;", null, "Hello World")
        strFiledVisitor.visitEnd()

        super.visit(version, access, name, signature, superName, interfaces)
    }

3.2 visitEnd方法中

override fun visitEnd() {

        val nameFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "name", "Ljava/lang/String;", null, "jdsjlzx")
        nameFiledVisitor.visitEnd()

        super.visitEnd()
    }

4. 动态添加字段的初始化

通过ASM添加的字段,需要在ini方法中进行赋值

 override fun onMethodEnter() {
        println("onMethodEnter access $access className $className name $name  descriptor $descriptor")


        //动态生成的变量要在init方法中赋值(static变量除外)
        if (name.equals("<init>") && descriptor.equals("()V")) {
            //每个classFile都有一个<init>的初始化方法(固定写法)
            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
            mv.visitVarInsn(Opcodes.ALOAD, 0)


            //初始化定义的变量的值
            mv.visitIntInsn(BIPUSH, 100)
            mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "age", "I" )

            //String的初始化会在构造方法中
            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitLdcInsn("Hello World")
            mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "str", "Ljava/lang/String" )

            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitLdcInsn("jdsjlzx")
            mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "name", "Ljava/lang/String" )

            mv.visitInsn(RETURN)
        }

     
        super.onMethodEnter()
    }

插桩后的class文件如下:

源码:jdsjlzx/AsmPluginProject

完整代码如下:

package com.baidu.plugin.print

import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.Attribute
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.commons.AdviceAdapter


class PrintClassVisitor(nextVisitor: ClassVisitor, private val className: String): ClassVisitor(ASM9, nextVisitor),
    Opcodes {

    override fun visit(
        version: Int,
        access: Int,
        name: String?,
        signature: String?,
        superName: String?,
        interfaces: Array<out String>?
    ) {
        println("visit access $access className $className name $name  signature $signature")

        val numFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "age", "I", null, 25)
        numFiledVisitor.visitEnd()
        val strFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "str", "Ljava/lang/String;", null, "Hello World")
        strFiledVisitor.visitEnd()

        super.visit(version, access, name, signature, superName, interfaces)
    }


    override fun visitEnd() {

        val nameFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "name", "Ljava/lang/String;", null, "jdsjlzx")
        nameFiledVisitor.visitEnd()

        //准备添加字段age的setAge以及getAge方法(添加之前确保age字段存在)
        val hasAgeField = true;
        if (hasAgeField) {

            var methodVisitor = cv.visitMethod(ACC_PUBLIC, "getAge", "()I", null, null);
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitFieldInsn(GETFIELD, "com/baidu/main/MainActivity", "age", "I");
            methodVisitor.visitInsn(IRETURN);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();


            methodVisitor = cv.visitMethod(ACC_PUBLIC, "setAge", "(I)V", null, null);
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitVarInsn(ILOAD, 1);
            methodVisitor.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "age", "I");
            methodVisitor.visitInsn(RETURN);
            methodVisitor.visitMaxs(2, 2);
            methodVisitor.visitEnd();


        }




        super.visitEnd()
    }


    override fun visitField(
        access: Int,
        name: String?,
        descriptor: String?,
        signature: String?,
        value: Any?
    ): FieldVisitor {
        //动态生成的file不会触发visitField方法,除非类中有声明的field
        println("visitField access $access className $className name $name  descriptor $descriptor")





        val filedVisitor = super.visitField(access, name, descriptor, signature, value)

        return MyPrintFieldVisitor(
            Opcodes.ASM5,
            filedVisitor
        )
    }

    override fun visitMethod(
        access: Int,
        name: String?,
        descriptor: String?,
        signature: String?,
        exceptions: Array<out String>?
    ): MethodVisitor {
        val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)

        return MyPrintMethodVisitor(
            Opcodes.ASM5,
            methodVisitor,
            access,
            className,
            name,
            descriptor
        )
    }

}

class MyPrintFieldVisitor(api: Int, filedVisitor: FieldVisitor) : FieldVisitor(api, filedVisitor) {

    override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
        println("visitAnnotation descriptor $descriptor visible $visible")


        return super.visitAnnotation(descriptor, visible)
    }


}

class MyPrintMethodVisitor(
    api: Int,
    methodVisitor: MethodVisitor?,
    private val access: Int,
    private val className: String,
    private val name: String?,
    private val descriptor: String?
) : AdviceAdapter(api, methodVisitor, access, name, descriptor) {


    override fun onMethodEnter() {
        println("onMethodEnter access $access className $className name $name  descriptor $descriptor")


        //动态生成的变量要在init方法中赋值(static变量除外)
        if (name.equals("<init>") && descriptor.equals("()V")) {
            //每个classFile都有一个<init>的初始化方法(固定写法)
            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
            mv.visitVarInsn(Opcodes.ALOAD, 0)


            //初始化定义的变量的值
            mv.visitIntInsn(BIPUSH, 100)
            mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "age", "I" )

            //String的初始化会在构造方法中
            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitLdcInsn("Hello World")
            mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "str", "Ljava/lang/String" )

            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitLdcInsn("jdsjlzx")
            mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "name", "Ljava/lang/String" )

            mv.visitInsn(RETURN)
        }

        if (name.equals("sum999")) {
            //System.out.println("999999");
            mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("999999-begin")
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        }




        super.onMethodEnter()
    }

    override fun onMethodExit(opcode: Int) {
        println("onMethodExit access $access className $className name $name  descriptor $descriptor")
        if (name.equals("sum999")) {
            //System.out.println("999999");
            mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("999999-end")
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        }
        super.onMethodExit(opcode)
    }

    override fun visitFieldInsn(opcode: Int, owner: String?, name: String?, descriptor: String?) {
        println("visitFieldInsn opcode $opcode owner $owner name $name  descriptor $descriptor")
        super.visitFieldInsn(opcode, owner, name, descriptor)
    }

    override fun visitLocalVariable(
        name: String?,
        descriptor: String?,
        signature: String?,
        start: Label?,
        end: Label?,
        index: Int
    ) {
        println("visitLocalVariable name $name  descriptor $descriptor start $start end $end index $index")
        super.visitLocalVariable(name, descriptor, signature, start, end, index)
    }

    override fun visitAttribute(attribute: Attribute?) {
        println("visitAttribute attribute $attribute")
        super.visitAttribute(attribute)
    }

    override fun visitLineNumber(line: Int, start: Label?) {
        println("visitLineNumber line $line start $start")
        super.visitLineNumber(line, start)
    }

    override fun visitFrame(
        type: Int,
        numLocal: Int,
        local: Array<out Any>?,
        numStack: Int,
        stack: Array<out Any>?
    ) {
        println("visitFrame type $type  numLocal $numLocal local $local numStack $numStack stack $stack")
        super.visitFrame(type, numLocal, local, numStack, stack)
    }

    override fun visitLabel(label: Label?) {
        println("visitLabel label $label")
        super.visitLabel(label)
    }


    override fun visitCode() {
        println("visitCode")

        super.visitCode()
    }

    override fun visitMaxs(maxStack: Int, maxLocals: Int) {
        println("visitMaxs maxStack $maxStack maxLocals $maxLocals")
        super.visitMaxs(maxStack, maxLocals)
    }

    override fun visitEnd() {
        println("visitEnd")
        super.visitEnd()
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1869242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

触摸屏与罗克韦尔AB PLC之间 ModbusTCP/IP无线以太网通讯实例

在实际系统中&#xff0c;同一个车间里分布多台PLC&#xff0c;通过触摸屏人机界面集中控制。通常所有设备距离在几十米到上百米不等。在有通讯需求的时候&#xff0c;如果布线的话&#xff0c;工程量较大耽误工期&#xff0c;这种情况下比较适合采用无线通信方式。本方案以组态…

阿里云oss存储

文章目录 准备阿里云的OSS控制台创建bucket获取AccessKey java使用oss导入依赖官网demo修改参数运行demo代码 封装工具类Oss下载如何保证指定时间段内可以访问私有权限的图片文件&#xff1f; 准备阿里云的OSS 控制台 访问阿里云官网&#xff0c;登录以后&#xff0c;右上角有…

Omniverse 下载 isaac sim过慢的解决办法

比如在上海地区&#xff0c;下载isaac只有 200kb/s&#xff0c;这8个G下载要很长时间 对于着急的小伙伴&#xff0c;可以直接去日志里拿下载链接&#xff0c;在Omniverse里点右上角小人&#xff0c;点开里面SETTINGS&#xff0c;如图 点击&#xff0c;LOGS LOCATION&#xff0c…

校企合作,为人才培养注入新动力

树莓集团在校企合作育人方面取得了显著成效&#xff0c;通过共建专业、定制课程、实习实训等多种方式&#xff0c;实现了教育资源的优化配置和高效利用&#xff0c;为高校和企业提供了更多的发展机会和合作空间。 1、共建专业与实验室&#xff1a;树莓集团与高校共同建设数字产…

U-boot相关基础知识

U-boot和Bootloader之间的关系 U-Boot是Bootloader的一种实现&#xff0c;它专门用于嵌入式系统&#xff0c;特别是那些基于ARM、MIPS等处理器的系统。U-Boot提供了丰富的硬件支持和功能&#xff0c;使得开发者能够轻松地初始化硬件、加载操作系统内核&#xff0c;并进行一些基…

反向代购是怎么火起来的?今后的发展趋势如何?

反向代购和反向海淘的兴起可以归因于多个因素&#xff0c;这些因素共同推动了海外消费者对中国商品的需求和购买热潮。以下是对其火起来的原因的详细分析&#xff1a; 海外华人华侨的需求增加&#xff1a; 随着中国国际移民群体的扩大&#xff0c;海外华人华侨数量不断增多。这…

GD32F303 使用PA8输出内部时钟频率

前面给小伙伴介绍过串口发送和接收异常可能的一些原因&#xff0c;其中就有说到时钟频率对于异步通讯的重要性。而我们通过程序去配置的时钟都是理论值&#xff0c;那如果想要获得内部一些时钟频率的实际值&#xff0c;需要怎样做呢&#xff1f;今天&#xff0c;我们以GD32F303…

小项目——MySQL集训(学生成绩录入)

ddl语句 -- 创建学生信息表 CREATE TABLE students (student_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 学生ID,name VARCHAR(50) NOT NULL COMMENT 学生姓名,gender ENUM(男, 女) NOT NULL COMMENT 性别,class VARCHAR(50) NOT NULL COMMENT 班级,registration_date DATE CO…

RabbitMQ实践——定制一致性Hash交换器的路由字段

大纲 Property法定制交换器绑定队列测试 Header法定制交换器绑定队列测试 代码工程参考资料 在《RabbitMQ实践——利用一致性Hash交换器做负载均衡》一文中&#xff0c;我们熟悉了一致性Hash交换器的使用方法。默认的&#xff0c;它使用Routing key来做Hash的判断源。但是有些时…

基于Python的数码产品销售平台

1 项目介绍 1.1 研究目的和意义 本研究旨在设计和实现一个基于Python的数码产品销售平台&#xff0c;其核心目的在于通过先进的技术手段&#xff0c;提升数码产品销售的效率和用户体验&#xff0c;进而推动数码产品市场的繁荣发展。通过利用Python这一强大且灵活的编程语言&a…

可的哥视频会议(Meeting): 开启智能云端会议新纪元!

随着远程办公和全球化协作需求的不断增长&#xff0c;企业亟需一种更高效、更便捷的会议解决方案。在这样的背景下&#xff0c;可的哥&#xff08;Codigger&#xff09;视频会议&#xff08;Meeting&#xff09;应运而生&#xff0c;为企业提供了全新的沟通与协作方式。 可的哥…

AUTOSAR以太网之IPv4

系列文章目录 返回总目录 文章目录 系列文章目录一、IPv4报文格式二、主要函数1.IPv4_Init()2.IPv4_Receive()3.IPv4_Transmit()一、IPv4报文格式 二、主要函数 1.IPv4_Init() 这个函数除了对模块配置进行初始化,如果有分包和组包使能,则会对一些相关配置进行初始化如buf长…

Codeforces Round 954 (Div. 3) A~F

A.X Axis&#xff08;暴力&#xff09; 题意&#xff1a; 在 X X X轴&#xff08; 1 ≤ x i ≤ 10 1\leq x_i\leq 10 1≤xi​≤10&#xff09;上有三个点&#xff0c;其整数坐标分别为 x 1 x_1 x1​、 x 2 x_2 x2​和 x 3 x_3 x3​。您可以选择 X X X轴上任何一个整数坐标为 …

免费恢复微信好友的聊天记录(已删除的好友不能恢复)

非常简单,适用于未删除的微信好友的聊天记录恢复,支持导出 1、下载楼月微信聊天记录导出恢复助手 - 导出手机微信聊天记录 2、官方原文教程链接&#xff1a;官方原文教程链接https://www.louyue.com/weixin.htm

高考填报志愿,要做到知己知彼兼顾平衡

寒窗苦读&#xff0c;无非就是希望能够考上一所理想的大学&#xff0c;不过自从高考改革以后&#xff0c;高考结束后只是第一阶段&#xff0c;接下来第二阶段应对高考填报志愿也同样重要。 如何选择合适的院校、专业&#xff0c;考生和家长都需要做好充足的准备&#xff0c;在收…

视频组合其他内容生成二维码的方法,多内容二维码的生成技巧

现在通过二维码来播放视频的使用场景越来越多&#xff0c;通过这种方式能够更加简单便捷的让用户获取内容&#xff0c;无需下载视频内容&#xff0c;有效提升用户的体验效果。那么在制作视频二维码时&#xff0c;怎么加入其他的内容呢&#xff0c;比如图片、文件、文本、音频等…

springboot校BA篮球网站-计算机毕业设计源码29210

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;校BA篮球网站当然也不能排除在外。校BA篮球网站是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#x…

【LeetCode】五、哈希表相关:统计重复元素 + 找不同

文章目录 1、哈希表结构2、Java中的哈希表3、leetcode217&#xff1a;统计重复元素4、leetcode389&#xff1a;找不同5、leetcode496&#xff1a;下一个更大元素 1、哈希表结构 又叫散列表&#xff0c;存键值对&#xff0c;将key用哈希函数转为数组下标索引 当两个不同的key经…

【代码安全】如何通过实现代码加密与魔改Python,防止代码泄露、恶意窃取

如何通过实现代码加密与魔改Python&#xff0c;防止代码泄露、恶意窃取 文章目录 如何通过实现代码加密与魔改Python&#xff0c;防止代码泄露、恶意窃取前言概述代码运行演示Step 0: 正常代码运行Step 1: 代码加密Step 2: 加密代码在魔改环境运行Step 3: 加密代码在正常环境运…

数字孪生如何赋能智慧加油站?

在当今数字化转型的浪潮中&#xff0c;智慧城市的构建正以前所未有的速度推进&#xff0c;而智慧加油站作为智慧城市生态系统的重要组成部分&#xff0c;其升级转型显得尤为重要。随着国家“十四五”规划对智慧城市和数字化建设的明确指引&#xff0c;以及“碳达峰、碳中和”目…