所以我最近一直在使用Javassist,我遇到了一个我无法找到答案的问题.CtMethod的insertAt方法允许您在特定的行号处插入代码,但是它会覆盖该行还是保留它,如何使它与默认情况相反?我有一个应用程序,它在运行时之前使用Javassist修改源代码,基于XML文件中的"钩子".我想这样做是为了可以覆盖一条线,或者可以在线上方放置一条线而不是覆盖它.显然有一些黑客的方法可以做到这一点,但我宁愿使用正确的方法.
CtMethod对象中存在的insertAt(int lineNumber,String src)方法允许在给定行中的代码之前注入在src中 编写的代码.
例如,采用以下(简单)示例程序:
public class TestSubject {
public static void main(String[] args) {
TestSubject testSubject = new TestSubject();
testSubject.print();
}
private void print() {
System.out.println("One"); // line 9
System.out.println("Two"); // line 10
System.out.println("Three"); // line 11
}
}
Run Code Online (Sandbox Code Playgroud)
通过简单编码(请记住,方法变量必须是print方法的CtMethod表示):
// notice that I said line 10, which is where the sysout of "two" is
method.insertAt(10, true, "System.out.println(\"one and an half\");");
Run Code Online (Sandbox Code Playgroud)
将在类中注入一个新的sysout指令.新类的输出将是:
one
one and an half
two
three
Run Code Online (Sandbox Code Playgroud)
Javassist没有提供一种简单的方法来删除一行代码,所以如果你真的想要替换它,你将别无选择,只能破解你的方式.
怎么做?好吧,让我向你介绍你的新朋友(如果你还不知道),CodeAttribute对象.
CodeAttribute对象负责保存表示方法流的字节码,此外该代码属性还有另一个名为LineNumberAttribute的属性,可帮助您将行号映射到字节码数组中.所以总结这个对象有你需要的一切!
以下示例中的想法非常简单.将字节码数组中的字节与应删除的行相关联,并用无操作代码替换字节.
方法再次是方法打印的CtMethod表示
// let's erase the sysout "Two"
int lineNumberToReplace = 10;
// Access the code attribute
CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
// Access the LineNumberAttribute
LineNumberAttribute lineNumberAttribute = (LineNumberAttribute) codeAttribute.getAttribute(LineNumberAttribute.tag);
// Index in bytecode array where the instruction starts
int startPc = lineNumberAttribute.toStartPc(lineNumberToReplace);
// Index in the bytecode array where the following instruction starts
int endPc = lineNumberAttribute.toStartPc(lineNumberToReplace+1);
System.out.println("Modifying from " + startPc + " to " + endPc);
// Let's now get the bytecode array
byte[] code = codeAttribute.getCode();
for (int i = startPc; i < endPc; i++) {
// change byte to a no operation code
code[i] = CodeAttribute.NOP;
}
Run Code Online (Sandbox Code Playgroud)
在原始 TestSubject类中运行此修改将导致注入类具有以下输出:
one
three
Run Code Online (Sandbox Code Playgroud)
当你有需要添加一条线,仍保持现有的一个,你只需要使用给出的例子比较容易的部分,如果你想更换线路,你必须先删除使用中给出的例子既有线的硬件然后使用第一个例子注入新行.
还要记住,在示例中我假设您已经熟悉了javassist的基础知识,只显示了多汁的部分,而不是所有的交易.这就是为什么,例如,在示例中没有ctClass.writeFile ......你仍然需要这样做,我只是把它留下来因为我确实你应该知道你必须这样做.
如果您在代码示例中需要任何额外帮助,请询问.我很乐意帮忙.
| 归档时间: |
|
| 查看次数: |
1893 次 |
| 最近记录: |