java Javassist 添加方法并调用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13797919/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javassist add method and invoke
提问by Korki Korkig
I'm stuck with javassist. I've added a new method to my object class on runtime.
我被 javassist 困住了。我在运行时向对象类添加了一个新方法。
My object class:
我的对象类:
package tmp3;
public class Car {
public Car(){}
}
My test class:
我的测试班:
package tmp3;
import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
public class TestMain {
public static void main(String[] args) {
try {
CtClass ctclass = ClassPool.getDefault().get("tmp3.Car");
CtMethod newmethod = CtNewMethod.make("public void testPrint() { System.out.println(\"test ok\"); }",ctclass);
ctclass.addMethod(newmethod);
ctclass.writeFile();
for(Method me: ctclass.toClass().getDeclaredMethods()){ //test print, ok
System.out.println(me.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
But after that point, I don't know how to call(invoke) it. I've read that javassist has not the capability to invoke methods. Then how can I invoke the method which I've just added with javassist?
但在那之后,我不知道如何调用(调用)它。我读过 javassist 没有调用方法的能力。那么我如何调用我刚刚用 javassist 添加的方法呢?
I've tried lots of things in two days but had no success. Could you please help me with this?
我在两天内尝试了很多东西,但没有成功。你能帮我解决这个问题吗?
回答by pabrantes
Java classes have static interfaces which means, as you probably already know, Java is not designed by default to add methods to a class at runtime so it's a bit tricky, but not that hard to achieve what you want.
Java 类具有静态接口,这意味着,正如您可能已经知道的那样,Java 并不是默认设计为在运行时向类添加方法,因此它有点棘手,但实现您想要的并不难。
You've used Javassist, a bytecode modifier framework, to engineer your compiled class to add more bytecode that represents a new method. You can have one of the two scenarios:
您已经使用 Javassist(一个字节码修改器框架)来设计您的编译类以添加更多表示新方法的字节码。您可以有以下两种情况之一:
Scenario 1: Code which is compiled along with your Car class before injection
场景 1:在注入前与 Car 类一起编译的代码
In this case, when your code is being compiled the Java Compiler only knows the Car
interface without any injections. So you can'tjust invoke the injected method directly, like this:
在这种情况下,当您的代码被编译时,Java 编译器只知道Car
接口而不进行任何注入。所以你不能直接调用注入的方法,像这样:
Car car = new Car();
car.testPrint();
You have to do it by reflection like @Scorpion correctly commented:
你必须像@Scorpion 正确评论的那样通过反射来做到这一点:
Car car = new Car();
Method method = car.getClass().getMethod("testPrint", new Class[]{});
method.invoke(car,new Object[]{});
But this is not the only way...
但这不是唯一的方法......
Scenario 2: Code which USES your compiled and injected Class
场景 2:使用您编译和注入的类的代码
If you compile your Car
class, inject it and afterwardswrite code against the compiled class (for example having the Car
class in a jar file) you'll be able to call your injected method as if it were any other regular method.
如果你编译你的Car
类,注入它,然后针对编译的类编写代码(例如将Car
类放在 jar 文件中),你将能够像调用任何其他常规方法一样调用注入的方法。
Do the following exercise:
做以下练习:
- Compile your
Car
class - Run your TestMain which will do the injection
- Create another project in your IDE and add to that project's classpath the directory with the injected class ORcreate a jar with only the injected class and add that jar to the classpath
- Create a class in the new project that creates a new
Car
instance, notice that you're now able to invoke testPrint method without any hassle.
- 编译你的
Car
类 - 运行将执行注入的 TestMain
- 在您的 IDE 中创建另一个项目并将包含注入类的目录添加到该项目的类路径或创建一个仅包含注入类的 jar 并将该 jar 添加到类路径
- 在创建新
Car
实例的新项目中创建一个类,请注意您现在可以轻松调用 testPrint 方法。
A few things you should keep attention:
你应该注意的几件事:
- If you're overwriting your original class with the injected class, you might end up with an invalid class resulting in a
java.lang.ClassFormatError
with an error message saying that you have a Truncated Class file. This happens if Javassist hasn't loaded all the bytecode to memory and tried to write and read to and from the same class file, which results in a total mess. To avoid this, you can either write to a different path or make sure you load all the bytecode to memory before writing the file (use thetoByteCode()
fromCtClass
) . - If you have two class files, one with the injected code and one with the original code, remember to have only one in your classpath.
- 如果您用注入的类覆盖原始类,您可能最终会得到一个无效的类,导致出现一条
java.lang.ClassFormatError
错误消息,指出您有一个截断的类文件。如果 Javassist 没有将所有字节码加载到内存中并尝试从同一个类文件中写入和读取,就会发生这种情况,这会导致完全混乱。为了避免这种情况,您可以写入不同的路径,或者确保在写入文件之前将所有字节码加载到内存中(使用toByteCode()
fromCtClass
)。 - 如果您有两个类文件,一个包含注入的代码,一个包含原始代码,请记住在您的 classpath 中只有一个。
回答by user5212824
Use java reflection to invoke it, javassist for the class modification and loading ... then java reflection to run it (invoke ...).
使用java反射来调用它,javassist用于类修改和加载......然后使用java反射来运行它(调用......)。