The code is in Scala, but hopefully understandable for Java programmers as well.
I have the following class:
class SampleClass {
def test(in: String) = "Hello world!"
}
I have the following code that creates an enhanced instance of this class:
val e = new Enhancer
e.setSuperclass(classOf[SampleClass])
e.setCallback(new Cb)
println(e.create.asInstanceOf[SampleClass].test("foo"))
The callback is as follows:
class Cb extends MethodInterceptor {
override def intercept(obj: Any, m: Method, args: Array[Object], proxy: MethodProxy): Object = {
println(s"$m is called: ${m.getName}")
proxy.invokeSuper(obj, args)
}
}
This works fine. Now, if I instead try to create a class and then a new instance of it, all the methods are called directly, bypassing the callback:
val e = new Enhancer
e.setSuperclass(classOf[SampleClass])
e.setCallbackType(classOf[Cb])
println(e.createClass.newInstance.asInstanceOf[SampleClass].test("foo"))
Why so? How do I create a java.lang.Class
and use its newInstance()
method to generate the enhanced objects?
Solved it. You need to instantiate the instance, but avoid calling its constructor in process. Then you bind callbacks manually by invoking its private methods:
val instance = instantiator.newInstance.asInstanceOf[SampleClass]
val bc = subclass.getDeclaredMethod("CGLIB$BIND_CALLBACKS", classOf[Object])
val stc = subclass.getDeclaredMethod("CGLIB$SET_THREAD_CALLBACKS", classOf[Array[Callback]])
bc .setAccessible(true)
stc.setAccessible(true)
stc.invoke(null, Array[Callback](new Cb))
bc .invoke(null, instance)
stc.invoke(null, null)
instantiator
here is of type ObjectInstantiator - use Objenesis to avoid calling the constructor. subclass
here is the subclass generated by e.createClass
.
Avoiding calling the constructors in the process is important, since the enhanced constructor will set the flag that the instance was created and it will be impossible to bind the callbacks. By the way, you should also set this flag after the whole operation, for consistency. Its name is CGLIB$CONSTRUCTED.
You can see how the enhanced constructor works here.