For question #1, yes, you can achieve this via modify ApiFacadeImpl
's constructor. Remember to use CtConstructor#insertAfter
to append the assignment statement.
ClassPool pool = ClassPool.getDefault();
CtClass factoryClass = pool.getCtClass("ServiceFactory");
CtConstructor constructor = factoryClass.getDeclaredConstructor(null);
String setMockStatement = String.format("service = new %s();",
MockServiceImpl.class.getCanonicalName());
constructor.insertAfter(setMockStatement);
factoryClass.toClass();
new ServiceFactory().getService().say();
I tried the way you proposed. However, it didn't work. After some debuggings, I found that if we remove a field, it won't remove the initialization statement for this field. If we add the field again with our expected initialization statement, it will executed before the original initialization statement. So the service
field is assigned to MockServiceImpl
at first and then assigned to null
. Please refer to the following javassist bug.
For question #2, I am not sure why this happens. What is your javassist version? How does your mockClass look like? Could you please refer to the following javassist bug?
Javassist causes java.lang.ClassFormatError: Invalid length 561 in LocalVariableTable in class file