首先让我解释一下我的目标...我需要更改方法体的部分,我的意思是,在我有几个调用的方法中,但只需要将其中一个调用更改为使用两个参数而不仅仅是1.因为我想在方法体内保持所有其余部分相同的方法体,通过regexp改变这个特定的调用我想改变并重新设置它。
假设这是做到这一点的好方法......我怎样才能获得方法体?
任何建议都会非常感激
由于
Raphael Moita
答案 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 工具在修改之前反汇编编译的类,并与生成的字节代码进行比较。以这种方式查找错误更容易。您还可以找到要以这种方式修改的字节代码指令。