About Martin Mois

Martin is a Java EE enthusiast and works for an international operating company. He is interested in clean code and the software craftsmanship approach. He also strongly believes in automated testing and continuous integration.

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:

2014-01-14_proxies_chart

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.
 

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

6 Responses to "Implementing dynamic proxies – a comparison"

  1. Curios Coder says:

    What were the measurements in for the test?

  2. Robert says:

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

    }

  3. Martin says:

    Hello Robert,

    a simple Delegate implementation is exactly what is shown in the figure with the label “No proxy”.

    • Robert says:

      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?

Leave a Reply


× 5 = forty five



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books