Implementing dynamic proxies – a comparison
Sometimes there is the need to intercept certain method calls in order to execute your own logic everytime the intercepted method is called. If you are not within in Java EE’s CDI world and don’t want to use AOP frameworks like aspectj, you have a simple and similar effective alternative.
Since version 1.5 the JDK comes with the class java.lang.reflect.Proxy that allows you to create a dynamic proxy for a given interface. The InvocationHandler that sits behind the dynamically created class is called everytime the application invokes a method on the proxy. Hence you can control dynamically what code is executed before the code of some framework or library is called.
Next to JDK’s Proxy implementation bytecode frameworks like javassist or cglib offert similar functionality. Here you can even subclass an exiting class and decide which methods you want to forward to the superclass’s implementation and which methods you want to intercept. This comes of course with the burden of another library your project depends on and that may have to be updated from time to time whereas JDK’s Proxy implementation is already included in the runtime environment.
So let’s take a closer look and try these three alternatives out. In order to compare javassist’s and cglib’s proxy with the JDK implementation we need an interface that is implemented by a simple class, because the JDK mechanism only supports interfaces and no subclassing:
public interface IExample { void setName(String name); } public class Example implements IExample { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
In order to delegate the method calls on the proxy to some real object, we create an instance of the Example class above and call it within the InvocationHandler via a final declared variable:
final Example example = new Example(); InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(example, args); } }; return (IExample) Proxy.newProxyInstance(JavaProxy.class.getClassLoader(), new Class[]{IExample.class}, invocationHandler);
As you can see from the code sample the creation of a proxy a rather simple: Call the static method newProxyInstance() and provide a ClassLoader, an array of interfaces that should be implemented by the proxy as well as an instance of the InvocationHandler interface. Our implementation forwards for the sake of demonstration only the instance of Example we have created before. But in real life you can of course perform more advanced operations that evaluate for example the methods name or its arguments.
Now we take a look at the way the same is done using javassist:
ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(Example.class); Class aClass = factory.createClass(); final IExample newInstance = (IExample) aClass.newInstance(); MethodHandler methodHandler = new MethodHandler() { @Override public Object invoke(Object self, Method overridden, Method proceed, Object[] args) throws Throwable { return proceed.invoke(newInstance, args); } }; ((ProxyObject)newInstance).setHandler(methodHandler); return newInstance;
Here we have a ProxyFactory that wants to know for which class it should create a subclass. Then we let the ProxyFactory create a whole class that can be reused as many times as necessary. The MethodHandler is here analog to the InvocationHandler the one that gets called for each method invocation of the instance. Here again we just forward the call to an instance of Example we have created before.
Last but not least let’s take a look at cglib’s proxy:
final Example example = new Example(); IExample exampleProxy = (IExample) Enhancer.create(IExample.class, new MethodInterceptor() { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return method.invoke(example, args); } }); return exampleProxy;
In the cglib world we have an Enhancer class that we can use to implement a given interface with a MethodInterceptor instance. The implementation of the callback method looks very similar to the one in the javassist example. We just forward the method call via reflection API to the already existing instance of Example.
Now that we have seen three different implementations we also want to evaluate their runtime behavior. Therefore we write s simple unit test, that measures the execution time of each of these implementations:
@Test public void testPerformance() { final IExample example = JavaProxy.createExample(); long measure = TimeMeasurement.measure(new TimeMeasurement.Execution() { @Override public void execute() { for (long i = 0; i < JavassistProxyTest.NUMBER_OF_ITERATIONS; i++) { example.setName("name"); } } }); System.out.println("Proxy: "+measure+" ms"); }
We choose a huge number of iterations in order to stress the JVM and to let the HotSpot compiler create native code for the often executed passages. The following chart shows the average runtime of the three implementations:
To show the impact of a Proxy implementation at all, the chart also shows the execution times for the standard invocation of the method on the Example object (“No proxy”). First of all we can put to record that the proxy implementations are about 10 times slower than the plain invocation of the method itself. But we also notice a difference between the three proxy solutions. JDK’s Proxy class is surprisingly nearly as fast as the cglib implementation. Only javassist pulls out with about twice the exeuction time of cglib.
Conclusion: Runtime proxies are easy to use and you have different way of doing it. JDK’s Proxy only supports proxies for interfaces whereas javassist and cglib allow you to subclass existing classes. The runtime behavior of a proxy is about 10 times slower than a standard method invocation. The three solutions also differ in terms of runtime.
What were the measurements in for the test?
ms of course ;)
Sorry, noticed it in the sys out of the test now. Curios, I was surprised by the time to proxy using JavaAssist.
For completeness, I think you are missing a crucial comparison… what if you had a “manual proxy” that simply delegated the call… e.g.
class ManualProxy implements IExample
{
private final IExample upstream;
ManualProxy(IExample upstream)
{
this.upstream=upstream;
}
public void setName(String name)
{
upstream.setName(name);
}
}
Hello Robert,
a simple Delegate implementation is exactly what is shown in the figure with the label “No proxy”.
According to the write up, “no proxy” was using the example object directly, so I suspect that a “manual proxy” would be roughly twice that.
BTW, is there a link to download this source, or must I stitch it together from the segments to replicate the test?