Here I required to create a instance of BImpl
but BImpl
requires to access functionality by an interface A
. For this purpose, the class implements this interface A
. How Can I wire a delegation of these interface methods of BImpl
at runtime ? The idea is that BImpl
can use A
's methods.
In my case A
is known and the AImpl
instance is created at run-time.
public static void main(String[] args) {
B b = (B) Enhancer.create(BImpl.class, new MyInterceptor());
System.out.println(b.cellValue());
}
interface A {
String value();
}
class AImpl implements A {
@Override
public String value() {
return "MyA";
}
}
interface B {
String cellValue();
}
abstract class BImpl implements B, A {
@Override
public String cellValue() {
return value() + "MyBCell";
}
}
class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println(method.getName());
if ("value".equals(method.getName()))
return method.invoke(obj, args);
else
return proxy.invokeSuper(obj, args);
}
}
What you describe sounds like a mixin pattern. Can you not simply implement a delegation explicitly? Something like:
class BImpl implements B, A {
private A a;
public BImpl(A a) {
this.a = a;
}
@Override
public String cellValue() {
return value() + "MyBCell";
}
@Override
public String value() {
return a.value();
}
}
This should be possible since you say that you know A
during compilation.
Otherwise, your approach is probably the correct one for cglib if you are really need to do this at runtime. However, you need to hand a different instance for obj
when calling:
return method.invoke(obj, args);
Otherwise you call the intercepted method again and find yourself in an endless loop by hitting MyMethodInterceptor
in a loop. You could also safe yourself the branch and use a CallbkackFilter
instead.
If you are not bound to cglib, you could also look into my library Byte Buddy which makes this all a little bit more expressive:
new ByteBuddy()
.subclass(BImpl.class)
.method(isAnnotatedBy(A.class))
.intercept(Forwarding.to("a", A.class))
.make();
The generated class has now a field a
which you can set to an instance of any A
to which the method invocations of the A
interface methods are now delegated. You can set this field by for example using Java reflection. Otherwise, you can use a static
field by
Forwarding.to(new A() {
// A methods
});