I want to create a dynamic proxy, which can delegate its methods to different implementations (each method invocation chooses a potentially different object). And I want to achieve the polymorphic effect, like when some proxied method calls another proxied method, the object selection mechanism does apply again.
Okay, enough confusion, here is an example:
interface IService {
void a();
void b();
}
class HappyService implements IService {
public void a() {
System.out.println("Happy a");
b();
}
public void b() {
System.out.println("Happy b");
}
}
class SadService implements IService {
public void a() {
System.out.println("Sad a");
b();
}
public void b() {
System.out.println("Sad b");
}
}
Now, I want to create a proxy for IService which always chooses HappyService for invocations of method a() and SadService for invocations of method b(). Here is what comes to my mind at first:
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke( final Object proxy, final Method method, final Object[] args ) throws Throwable {
Object impl;
if (method.getName().equals("a")) {
impl = new HappyService();
} else if (method.getName().equals("b")) {
impl = new SadService();
} else {
throw new IllegalArgumentException("Unsupported method: " + method.getName());
}
return method.invoke(impl, args);
}
};
IService service = (IService)Proxy.newProxyInstance( IService.class.getClassLoader(), new Class[]{ IService.class }, h );
service.a();
This prints:
Happy a
Happy b
Yeah, that’s because the invocation of b() inside of a() does not know anything about the dynamic proxy.
So, how do I best achieve my goal? My desired output is:
Happy a
Sad b
I could probably replace my new HappyService() inside the invocation handler with yet another proxy, which passes only method a() to HappyService, and redirects all other methods back to the original proxy. But maybe there is a better/easier solution?
A proxy can only be used to intercept inter-object calls, when the caller has a reference to the proxy instead of the “real” implementation. Here, both your implementations of
a()callb()directly, so of course they call it onthis. What you want to do cannot be achieved by proxies.However, you can do it using AOP, for example using AspectJ and either compile-time weaving (also available with the aspectj-maven-plugin) or load-time weaving. Roughly, you create an aspect with pointcuts on the calling sites of the
a()andb()methods in theIServiceimplementations, and you advise the execution, which might be to replace the original call by another. Untested code, but it looks like this:You can then provide directly an
HappyServiceorSadServiceimplementation as anIService, both have been modified. You can also create a single pointcut matching all the methods ofIService, and do the routing dynamically based on the method name (usingthisJoinPointin the advice), like in your example.Aspects can also be declared using annotations.