使用javassist更改方法体的一部分

时间:2012-09-21 15:30:33

标签: java javassist

首先让我解释一下我的目标...我需要更改方法体的部分,我的意思是,在我有几个调用的方法中,但只需要将其中一个调用更改为使用两个参数而不仅仅是1.因为我想在方法体内保持所有其余部分相同的方法体,通过regexp改变这个特定的调用我想改变并重新设置它。

假设这是做到这一点的好方法......我怎样才能获得方法体?

任何建议都会非常感激

由于

Raphael Moita

1 个答案:

答案 0 :(得分:2)

我今天必须在类的静态初始化程序中进行一些修改,并且可以使用 Javaasist 。但这很难。您需要至少掌握一些关于java字节代码生成/处理的知识(编译后不再有源代码)。

您可能会发现此维基百科页面非常有用:http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings。它包含所有Java字节代码指令的列表,它们采用的参数以及它们在堆栈中的作用。

这是代码( Groovy ,但应该很容易转换为Java)。它的作用是通过静态类初始化程序的字节代码,并删除以前在字节码中重命名的静态oldFieldName的所有赋值指令。希望你觉得它很有用。

def oldFieldName = "removedField"
def cp = ClassPool.getDefault()
//Create new class out of name and bytes
cp.insertClassPath(new ByteArrayClassPath(name, bytes))

def initializer = cc.classInitializer;                                                    
def mi = initializer.getMethodInfo();                                                     
def ca = mi.getCodeAttribute();                                                           

def ci = ca.iterator()                                                                    
//Let's check all bytecode operations in static initializer                               
while (ci.hasNext()) {                                                                    
    def index = ci.next()                                                                 
    int op = ci.byteAt(index)                                                             
    //PUTSTATIC is a bytecode instruction to assign value from stack to static variable   
    if (op == Opcode.PUTSTATIC) {                                                         
        //Address of target variable is calculated like this                              
        def targetFieldAddr = (ci.byteAt(index + 1) << 8) + ci.byteAt(index + 2)          
        def fieldrefName = mi.getConstPool().getFieldrefName(targetFieldAddr)             
        if (fieldrefName == oldFieldName) {                                               
            //Ok, so it's an assignment to renamed variable                               
            //Let's change assignemt to pop from stack (POP2 -> pop long/double value)           
            //We have to remove it or stack won't be valid                                   
            ci.writeByte((byte) Opcode.POP2, index);                                      
            //PUTSTATIC takes 2 arguments so we have to cleare them out          
            //or they will be used as bytecode instructions and probably invalidate class
            ci.writeByte((byte) Opcode.NOP, index + 1);                                   
            ci.writeByte((byte) Opcode.NOP, index + 2);                                   
    }                                                                                 
}                                                                                     

cc.defrost()
cc.detach()

另一个提示:使用JDK中的 javap 工具在修改之前反汇编编译的类,并与生成的字节代码进行比较。以这种方式查找错误更容易。您还可以找到要以这种方式修改的字节代码指令。