Cannot inject already loaded type on MethodDelegation returning same class
First of all, thank you very much for this library! It has a lot of features, and I read through documentation but can not find this particular feature. I need to override return value from method that returns this method's class. ClassA is from another library, so I can modify it.
class ClassA {
fun getInstance(): ClassA {
return ClassA()
}
}
class Interceptor {
fun getInstance(): ClassA {
println("intercepted")
return ClassA()
}
}
fun main() {
ByteBuddy()
.redefine<Any>(TypePool.Default.ofSystemLoader()
.describe("ClassA")
.resolve(), ClassFileLocator.ForClassLoader.ofSystemLoader())
.method(named("getInstance")).intercept(MethodDelegation.to(Interceptor()))
.make()
.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
println(ClassA().getInstance())
}
It works fine when return type is something else other that ClassA With return type ClassA getting "Cannot inject already loaded type: class ClassA" error. There's another way to override return value with Advice, however that does not work with Kotlin https://github.com/raphw/byte-buddy/issues/1014 Even when using Advice, getting same error, because ClassA is loaded when it loads Interceptor.
Would it be possible to modify this snippet or maybe there's another approach (without external agent)?
Many thanks
To do this, you would need to delegate to a field instead. MethodDelegation offsets this and you can make the field static if appropriate. For this field, you can use an unloaded TypeDescription which avoids loading the class.
Then, after loading the class, you can use reflection to set the field value.
Rafael, I appreciate the reply.
I'm struggling to implement what you described. Would it be possible to provide an approximate (pseudo) code or a link to some samples? Also, can MethodDelegation.toMethodReturnOf be used?
You'd: MethodDelegation.toField("myInterceptor") and also defineField in the DSL.
Thank you again for your reply. :smiley: I forgot to mention that the method to be intercepted is static, it looks more like this
public class ClassToIntercept {
static ClassToIntercept instance;
public static ClassToIntercept getInstance() {
return instance;
}
}
There are multiple instances of the class and there's a race condition between setting the field and reading it from the intercepting method. Are there any alternative approaches? Maybe with MemberSubstitution?
There's no way for this.
If you want to add code to the beginning of the method, you can avoid referencing ClassA in your interceptor and use andThen with SuperMethodCall.