您的位置:首页 > 其它

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

2013-08-07 14:59 459 查看
【前言】

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

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

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等等高难度技术点都可以实现了,下面先来看看如何添加函数:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: