Dynamic proxies have been part of Java since
Java SE 1.3. Here’s how the javadoc for the Proxy class defines it:
“A dynamic proxy class is a class that implements a list of
interfaces specified at runtime when the class is created.”
This means you can create a class at runtime
that implements one or more interfaces that you specify at runtime.
While you won’t need this functionality every day, it is useful if
you’re building a testing framework or object harness.
How dynamic proxies work
Dynamic proxies consist of two parts: an
invocation handler and a proxy instance. An invocation handler is a
class that implements the interface
java.lang.reflect.InvocationHandler. This is the class that will
complete or dispatch the work required by the interfaces defined by
the proxy instance. The proxy instance is an object that is passed
to the method requiring the type you’re emulating.
To create a proxy that implements a list of
interfaces, you use the static method newProxyInstance() of the
Proxy class. The newProxyInstance() method accepts three arguments:
an instance of ClassLoader, an array of class instances, and an
instance of InvocationHandler. The array of classes determines
which interfaces the new proxy instance will implement, and the
InvocationHandler will be invoked when methods are called on the
proxy instance.
The simple example below creates a class at
runtime that implements either the java.lang.Runnable or the
locally-defined Publisher interface:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ProxyTip {
public static void main(String args[])
throws Exception {
String className =
args[0];
Class c =
Class.forName(className);
InvocationHandler
handler = new UniversalHandler();
Object o =
Proxy.newProxyInstance(c.getClassLoader(),
new Class[] { c },
handler);
if (
className.equals(“java.lang.Runnable”) ) {
Thread
t = new Thread((Runnable) o);
t.start();
}
else if (
className.equals(“Publisher”) ) {
System.out.println(((Publisher) o).publish());
}
}
}
interface Publisher {
int publish();
}
class UniversalHandler implements InvocationHandler {
public Object invoke(Object proxy, Method
method, Object[] args) {
try {
System.out.println(“method invoked: ” + method);
Class
c = method.getReturnType();
System.out.println(“should
return type: ” + c);
if
( c.toString().equals(“void”) ) {
return
null;
}
else
if ( c.toString().equals(“int”) ) {
return
new Integer(0);
}
else
if ( c.toString().equals(“float”) ) {
return
new Float(0);
}
else
if ( c.toString().equals(“byte”) ) {
return
new Byte((byte) 0);
}
else
if ( c.toString().equals(“char”) ) {
return
new Character((char) 0);
}
else
if ( c.toString().equals(“short”) ) {
return
new Short((short) 0);
}
else
{
return
c.newInstance();
}
}
catch (Exception
e) {
e.printStackTrace();
}
return null;
}
}
One limitation of proxies is that only
interfaces may be proxied. If you try to create a proxy for a type
that isn’t an interface, you’ll get an exception like this:
Exception in thread “main”
java.lang.IllegalArgumentException: java.lang.String
is not an interface
The power of dynamic proxies is limited only by
our imagination and the number of interfaces available to you.
Check out the javadoc for more information about what to keep in
mind when using proxies.
Delivered each Thursday, our free Java newsletter provides insight and hands-on tips you need to unlock the full potential of this programming language. Automatically sign up today!