The principle of Separation of Concerns is one of the main aspects of modern application frameworks like Spring or Hibernate. The intention is to separate the cross-cutting-concerns (e.g. database access, transaction management or security checks) from the implementation of the functional requirements. One possible solution to realize a transparent separation of concerns is to use the proxy design pattern. A proxy is some kind of wrapper, which controls the access to the interface of any object. Therefore the proxy implements the same interface as the wrapped object. By using the proxy pattern you’re able to extend or change the behavior of an object without changing the object itself.
It’s quite simple to implement the proxy pattern in Java. The so called JDK proxies are part of the Java API since version 1.3. All you need is the Interface InvocationHandler and the helper class Proxy. Let’s take a look at a simple example. Given an interface:
public interface Service {
public void foobar();
}
There might be the following implementation of the interface for which you want to monitor the performance at runtime:
public class ServiceToBeMonitored implements Service {
@Override
public void foobar() {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Instead of writing the performance-measuring-code directly into the service implementation we’re separating this concern by using the proxy pattern. Therefor we implement the InvocationHandler
interface:
public class PerformanceMonitor implements InvocationHandler {
private final Object proxiedInstance;
public PerformanceMonitor(Object proxiedInstance) {
this.proxiedInstance = proxiedInstance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long t0 = System.nanoTime();
Object result = method.invoke(proxiedInstance, args);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println("Invocation of method " + method.getName() + "() took " + millis + " ms");
return result;
}
}
That’s it. Now we’re ready to create of proxy. The proxy delegates method calls to the wrapped object.
Service service = new ServiceToBeMonitored();
PerformanceMonitor handler = new PerformanceMonitor(service);
Service proxy = (Service) Proxy.newProxyInstance(
service.getClass().getClassLoader(), new Class[]{ Service.class }, handler);
Calling proxy.foobar() results in: Invocation of method foobar() took 201 ms