【前言】
还记得前面的那几个仿照官方编写的例子吗?今天的例子也是需要用到它。
首先,我们编写一个试验用的类,这个类就用来删除成员函数,添加成员函数等等。
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等等高难度技术点都可以实现了,下面先来看看如何添加函数: