【ASM的学习】asm官方手册学习心得02--动态添加或者删除类成员

【前言】

还记得前面的那几个仿照官方编写的例子吗?今天的例子也是需要用到它。




首先,我们编写一个试验用的类,这个类就用来删除成员函数,添加成员函数等等。


package TestCase;


public class MyTestClass {

    public String name="";
    public int age=0;
    public int getAge(){
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName(){
        return name;
    }

    public boolean isMale=false;

    public boolean isMale() {
        return isMale;
    }

    public void setMale(boolean male) {
        isMale = male;
    }

    public String getInfoMsg(){
        return "【名称】"+getName()+" 【年龄】"+getAge()+" 【是否男性】"+isMale();
    }

}


【删除成员函数】

删除成员函数,只需要在classreader进行遍历的时候,classvistor碰到某个函数名称便返回null,即可,具体实验代码:

【removeFunc--classvisitor的子类,重写了遍历方法时候的过滤操作】

package TestCase;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class removeFuncAdapter extends ClassVisitor {
    /**
     * 需要过滤的函数的名称
     * */
    private String _filterFuncName="";
    /**
     * 需要过滤的函数的参数描述信息,譬如:(I)V,(Ljava/lang/String;)I等等。
     * */
    private String _filterFuncDescription="";
    public removeFuncAdapter(String funcName,String funcDescription,ClassVisitor cv){
        super(Opcodes.ASM4,cv);

        _filterFuncName=funcName;
        _filterFuncDescription=funcDescription;
    }
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

        if(name.equals(_filterFuncName)){
            if(desc==null&&_filterFuncDescription==null){
                return null;
            }
            if(_filterFuncDescription!=null&&_filterFuncDescription.equals(desc)){
                return null;
            }
        }

        return cv.visitMethod(access, name, desc, signature, exceptions);
    }
}

【debugVisitor,用于打印字节码】

package TestCase;

import org.objectweb.asm.*;

/**
 * Created with IntelliJ IDEA.
 * User: Administrator
 * Date: 13-8-7
 * Time: 下午2:50
 * To change this template use File | Settings | File Templates.
 */
public class debugClassVistor extends ClassVisitor {
    public debugClassVistor(){
        super(Opcodes.ASM4);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        System.out.println("  "+name+" extends "+superName+"{");
    }

    @Override
    public void visitSource(String source, String debug) {

    }

    @Override
    public void visitOuterClass(String owner, String name, String desc) {

    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
          return super.visitAnnotation(desc,visible);
    }

    @Override
    public void visitAttribute(Attribute attr) {

    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {

    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        System.out.println(" "+desc+" " +name);
        return super.visitField(access,name,desc,signature,value);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        System.out.println(" "+name+""+desc);
        return  super.visitMethod(access,name,desc,signature,exceptions);
    }

    @Override
    public void visitEnd() {
        System.out.println("}");

    }
}

【主要操作函数】

package TestCase;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;

import java.io.File;

public class asm_del_memeber {
    public static void main(String[] args){

        try{
/**
 * 显示原始的字节码
 * **/

        System.out.println("====================原始字节码=============================");
ClassReader cr1=new ClassReader(MyTestClass.class.getName());
            cr1.accept(new debugClassVistor(),0);

        ClassReader cr=new ClassReader(MyTestClass.class.getName());

            ClassWriter cw=new ClassWriter(cr,0) ;
            removeFuncAdapter vistor1=new removeFuncAdapter("getInfoMsg","()Ljava/lang/String;",cw) ;
            cr.accept(vistor1,0);

            cw.visitEnd();



            /**
             * 显示修改以后的字节码
             * */
            System.out.println("====================修改后字节码============================");
ClassReader cr2=new ClassReader(cw.toByteArray());
            cr2.accept(new debugClassVistor(),0);


        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

}

【相关打印结果】

====================原始字节码=============================
  TestCase/MyTestClass extends java/lang/Object{
 Ljava/lang/String; name
 I age
 Z isMale
 <init>()V
 getAge()I
 setName(Ljava/lang/String;)V
 setAge(I)V
 getName()Ljava/lang/String;
 isMale()Z
 setMale(Z)V
 getInfoMsg()Ljava/lang/String;
}
====================修改后字节码============================
  TestCase/MyTestClass extends java/lang/Object{
 Ljava/lang/String; name
 I age
 Z isMale
 <init>()V
 getAge()I
 setName(Ljava/lang/String;)V
 setAge(I)V
 getName()Ljava/lang/String;
 isMale()Z
 setMale(Z)V
}

Process finished with exit code 0


【补充】

请注意,classwriter这个类也是classvisitor的子类,从上一篇文章我们可以调用visitField这种方法已经隐隐体验到了,现在将classwriter对象放到classvisitor的构造方法里竟然不报错,这就证明了我的想法。

【添加一个参数】

编写一个相关的classvisitor,然后再进行操作:

package TestCase;


import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;

public class addFieldAdapter extends ClassVisitor {
    private String _fieldName="";
    private String _fieldDesc="";
    private ClassVisitor _cv;
    private int _fAcc=0;
    public addFieldAdapter(ClassVisitor cv,int fACC,String fieldName,String fieldDesc){
        super(Opcodes.ASM4,cv);
        _fieldDesc=fieldDesc;
        _fAcc=fACC;
        _fieldDesc=fieldDesc;
        _fieldName=fieldName;
        _cv=cv;
    }

    @Override
    public void visitEnd() {
        FieldVisitor fieldVisitor=this.cv.visitField(_fAcc,_fieldName,_fieldDesc,null,null);
        if(fieldVisitor!=null){
            fieldVisitor.visitEnd();
        }
        cv.visitEnd();
    }
}

测试方法:

package TestCase;

import org.objectweb.asm.*;


import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import java.io.File;

public class asm_addField_member {
    public static void main(String[] args){

        try{
/**
 * 显示原始的字节码
 * **/

            System.out.println("====================原始字节码=============================");
            ClassReader cr1=new ClassReader(MyTestClass.class.getName());
            cr1.accept(new debugClassVistor(),0);



            ClassReader cr=new ClassReader(MyTestClass.class.getName());



            ClassWriter cw=new ClassWriter(cr,0) ;
            addFieldAdapter visitor1=new addFieldAdapter(cw, Opcodes.ACC_PUBLIC,"test_attribute_1","Ljava/lang/String;");


            //removeFuncAdapter vistor1=new removeFuncAdapter("getInfoMsg","()Ljava/lang/String;",cw) ;
            cr.accept(visitor1,0);

            cw.visitEnd();



            /**
             * 显示修改以后的字节码
             * */
            System.out.println("====================修改后字节码============================");
            ClassReader cr2=new ClassReader(cw.toByteArray());
            cr2.accept(new debugClassVistor(),0);


        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

}

【测试结果】


====================原始字节码=============================
  TestCase/MyTestClass extends java/lang/Object{
 Ljava/lang/String; name
 I age
 Z isMale
 <init>()V
 getAge()I
 setName(Ljava/lang/String;)V
 setAge(I)V
 getName()Ljava/lang/String;
 isMale()Z
 setMale(Z)V
 getInfoMsg()Ljava/lang/String;
}
====================修改后字节码============================
  TestCase/MyTestClass extends java/lang/Object{
 Ljava/lang/String; name
 I age
 Z isMale
 Ljava/lang/String; test_attribute_1
 <init>()V
 getAge()I
 setName(Ljava/lang/String;)V
 setAge(I)V
 getName()Ljava/lang/String;
 isMale()Z
 setMale(Z)V
 getInfoMsg()Ljava/lang/String;
}

Process finished with exit code 0
下篇预览:
【添加函数及修改函数】

下面这个内容肯定是重点了,也是我一直追求的,无论是我编写的代码生成器还是各个工具类库都缺不了灵活性,直接动态生成字节码修改运行方法,添加各种拦截器,reset框架,aop等等高难度技术点都可以实现了,下面先来看看如何添加函数:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值