修改class文件 (一)

com.jaredlin.sample.Animal.java

public class Animal {
    private String name;

    public String getName() {
        return name;
    }

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

    public void run() {
        System.out.println("My name is "+name+",I'm running");
    }

    @Override
    public String toString() {
        return this.name;
    }
}

使用jclasslib bytecode viewer 查看class文件

addField

public static void addField(){
    ClassPool pool = ClassPool.getDefault();
    try {
        CtClass ctClass = pool.get("com.jaredlin.sample.Animal");
        Utils.rename(ctClass,"AddField");
        CtClass type = ClassPool.getDefault().get("java.lang.String");
        CtField f = new CtField(type,"weight",ctClass);
        CtField.Initializer init = CtField.Initializer.constant("3kg");
        ctClass.addField(f,init);
        ctClass.writeFile();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void addField2() {
    ClassPool pool = ClassPool.getDefault();
    try {
        CtClass ctClass = pool.get("com.jaredlin.sample.Animal");
        CtField ctField = CtField.make("private String weight=\"3kg\";",ctClass);
        ctClass.addField(ctField);
        ctClass.setName("AddField2");
        ctClass.writeFile();
    } catch (NotFoundException e) {
        e.printStackTrace();
    } catch (CannotCompileException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

修改getName返回结果为Tiger

修改前getName方法对应的java源码

return name;

对应的Bytecode

其中 #2 表示获取到 Constant Pool 中的第二项 (Bytecode中的方法getfield必须获取的是CONSTANT_Fieldref_info) , 每行前面的红色数字表示当前这一行byte的起始位置 (后面修改的时候要用到 , 后面要修改的byte的index是3) .

为了使得getName方法的Bytecode改动量最小 , 需要新增一个field值为 "Tiger" 并直接替换为#2的位置 , 代码如下 (新增tiger , 查看ConstantPool确认tiger的序号 , 添加修改方法 ):

public static void changeReturn() {
     ClassPool pool = ClassPool.getDefault();
     try {
         CtClass ctClass = pool.getAndRename("com.jaredlin.sample.Animal","ChangeReturn");
         Utils.addStringField(ctClass,"tiger","Tiger");

         //修改方法开始 (修改方法需要新增的field在ConstantPool中的序号后执行)
         CtMethod method = ctClass.getDeclaredMethod("getName");
         CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
         CodeIterator iterator = codeAttribute.iterator();
         iterator.writeByte(57, 3);//tiger在ConstantPool中的序号
         //修改方法结束

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

参考 : http://www.cnblogs.com/sunfie/p/5154246.html

项目地址 https://github.com/jared-lin/JavassistSample

你可能感兴趣的:(修改class文件 (一))