让我先解释一下我的目标......我需要更改方法主体的一部分,我的意思是,在方法内部我有几个调用,但只需要更改其中一个调用以使用两个参数而不是 1。因为我想在我想的方法中保持所有其余部分相同,获取方法主体,通过正则表达式更改这个特定的调用,我想更改并重新设置它。
假设这是这样做的好方法......我怎样才能得到方法体?
任何建议将不胜感激
谢谢
拉斐尔·莫伊塔
今天我不得不对一个类的静态初始化器进行一些修改,并且可以使用Javaasist。不过还是挺难的。您至少需要对 java 字节码生成/处理有一些了解(编译后您不再有源代码)。
您可能会发现这个 Wikipedia 页面很有用: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工具在修改之前反汇编编译的类,并与您生成的字节码进行比较。这样更容易找到错误。您还可以通过这种方式找到要修改的字节码指令。