Reputation: 2814
I'm stuck with javassist. I've added a new method to my object class on runtime.
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?
I've tried lots of things in two days but had no success. Could you please help me with this?
Upvotes: 7
Views: 14423
Reputation: 1
Use java reflection to invoke it, javassist for the class modification and loading ... then java reflection to run it (invoke ...).
Upvotes: 0
Reputation: 2181
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.
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:
In this case, when your code is being compiled the Java Compiler only knows the Car
interface without any injections. So you can't just invoke the injected method directly, like this:
Car car = new Car();
car.testPrint();
You have to do it by reflection like @Scorpion correctly commented:
Car car = new Car();
Method method = car.getClass().getMethod("testPrint", new Class[]{});
method.invoke(car,new Object[]{});
But this is not the only way...
If you compile your Car
class, inject it and afterwards write 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.
Do the following exercise:
Car
classCar
instance, notice that you're now able to invoke testPrint method without any hassle.A few things you should keep attention:
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 the toByteCode()
from CtClass
) .Upvotes: 9