How to create a dynamic proxy with polymorphic delegation?

I want to create a dynamic proxy that can delegate its methods to different implementations (each method call selects a potentially different object). And I want to achieve a polymorphic effect, for example, when a proxied method calls another proxied method, the object selection mechanism is applied again.

Ok, 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 selects HappyService for invocations of a() method and SadService for calls to b() method. Here's what comes to my mind:

 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(); 

Fingerprints:

 Happy a Happy b 

Yes, this is because calling b() inside a() knows nothing about dynamic proxies.

So how do I best achieve my goal? My desired result:

 Happy a Sad b 

I could replace my new HappyService() inside the call handler with another proxy, which only passes the a() method to HappyService and redirects all the other methods back to the original proxy. But maybe there is a better / simpler solution?

+4
source share
1 answer

A proxy server can only be used to intercept calls between objects when the caller refers to a proxy instead of a "real" implementation. Here, both of your implementations of a() call b() directly, so of course they call it this . What you want to do cannot be reached by proxy.

However, you can do this with AOP, for example, using AspectJ and "compilation in time" (also available with aspectj-maven-plugin) or load time. For IService , you create an aspect with pointcuts on the calling sites of the a() and b() methods in IService implementations, and you report a run that could replace the original call with another. Unconfirmed code, but it looks like this:

 public aspect IServiceAspect { private IService happy = new HappyService(); // Assuming these are stateless services private IService sad = new SadService(); // that can be shared. // The pointcut is inside an IService implementation, and is a call to the a() method pointcut aCall(): within(IService+) && call(* IService.a()); pointcut bCall(): within(IService+) && call(* IService.b()); around(): aCall() && !within(HappyService) { // To avoid infinite recursion return happy.a(); } around(): bCall() && !within(SadService) { return sad.b(); } } 

You can then directly provide an implementation of HappyService or SadService as an IService , both have been changed. You can also create a single pointcut corresponding to all IService methods and perform dynamic dynamics based on the method name (using thisJoinPoint in the board), as in your example.

Aspects can also be announced using annotations .

+3
source

All Articles