Introduction to Java Dynamic Proxy
Bhaskar S | 11/04/2011 |
Overview
We have all had situations where we wanted to intercept calls to some method(s) and do interesting things like logging or measuring performance etc. Enter the Proxy design pattern.
The word proxy means substitute for another. In other words, a proxy is an object that implements the interface of a target object and acts as a substitute for the target object. Let us explore this popular design pattern with an example of an arbitrary Product Inventory checker application. Given a product code, find how many are in the inventory.
Hands-on with Code
The following is an interface for our Product Inventory checker:
The following is an arbitrary implementation of the ProductInventory interface that kind of mimics database access:
The following is an arbitrary application that uses the above Product Inventory checker implementation DbProductInventory:
After testing our arbitrary Product Inventory checker application, we release it to production for general use. Everything seems to go well until one day the users start complaining of slowness in using our Product Inventory checker application.
To analyze and troubleshoot the performance issue, we decide to measure the performance of the checkInventory method in our Product Inventory checker application. A simple way to achieve that would be to instrument the method checkInventory in our ProductInventory implementation class DbProductInventory as follows:
This would work, but what if we wanted to instrument many more method(s). Yikes !!! That is a lot of painstaking and cumbersome task. A better and cleaner approach would be to implement the same using the Proxy design pattern.
The following class implements the Proxy design pattern for our ProductInventory instance:
To use the Proxy implementation, we need to change the arbitrary application as follows:
See how clean and elegant it is !!! This is the power of the Proxy design pattern.
The one drawback to this approach is that we will need a proxy implementation for every interface that we wish to instrument in our application. Wouldn't it be great if we just had one class for instrumentation and use it dynamically at runtime across many interface(s) in our application !!! This is exactly what the Java Dynamic Proxy that was introduced in Java 1.3 does.
Java dynamic proxies allow one to dynamically create proxy classes on the fly at runtime for specified interface(s). All method calls on a dynamic proxy are dispatch to a single method called invoke on a single interface called the InvocationHandler. Every dynamic proxy class has a corresponding invocation handler class associated with it.
The following is the interface for InvocationHandler taken from the Java JDK source code for illustration purposes:
The InvocationHandler interface defines a single method called invoke. Every time a method from one the interface(s) of the dynamic proxy is called, it is dispatched to the invoke method on the corresponding implementation of the InvocationHandler.
The invoke method is called with the following arguments:
The following is our instrumentation class MethodTimingProxy that implements the InvocationHandler interface for measuring the checkInventory method in our Product Inventory checker application:
Notice that the above implementation of the InvocationHandler interface is generic in nature and does not have references to any of our Product Inventory checker application interface or classes.
Next, we will need a way to dynamically create an instance of the proxy for the interface ProductInventory. Remember that the proxy will need reference to an instance of MethodTimingProxy class. In order to create the dynamic proxy, we will use the following factory class:
The call to Proxy.newProxyInstance dynamically creates the byte code for the proxy at runtime and instantiates it in the Java Virtual Machine.
To use the Java Dynamic Proxy, we need to change the arbitrary application as follows:
Cool, ain't it !!! We can use the same MethodTimingProxy implementation to measure the performances of other method(s) in other interface(s) in our application.