Innovation

Create wrapper classes on the fly with Java dynamic proxies

Dynamic proxies allow Java coders to create wrappers on the fly to prevent malfunctions and save tedious code repetition. Find out what these coding shortcuts are capable of and learn how you can use them in your own applications.


The Java 1.3 release introduced a new feature called dynamic proxy classes, which provide a mechanism for creating wrapper classes on the fly for implementations of known interfaces. When I read about the proposed dynamic proxy classes before the 1.3 release, they struck me as a "gee whiz" feature. I was glad they were being included in the language, but I couldn't think of a single real use for them in systems I'd previously designed. But when I wrote a sample program using dynamic proxies to try them out, I was impressed with their power and stowed them in my toolbox for use on future projects. Since then, I've found instance after instance where they provided just the right way to do what needed to be done.

Without dynamic proxies
Before we look at what dynamic proxy classes are, let's examine how certain situations were handled before dynamic proxies were introduced.
 
public interface Robot {
    void moveTo(int x, int y);
    void workOn(Project p, Tool t);
}
 
public class MyRobot implements Robot {
    public void moveTo(int x, int y) {
        // stuff happens here
    }
    public void workOn(Project p, Tool t) {
        // optionally destructive stuff happens here
    }
}
 

The above code listing shows an interface named Robot and a roughed-in implementation of that interface named MyRobot. Imagine now that you want to intercept method calls made to the MyRobot class—perhaps limiting the value of a single parameter.
 
public class BuilderRobot implements Robot {
    private Robot wrapped;
    public BuilderRobot(Robot r) {
        wrapped = r;
    }
    public void moveTo(int x, int y) {
        wrapped.moveTo(x, y);
    }
    public void workOn(Project p, Tool t) {
        if (t.isDestructive()) {
            t = Tool.RATCHET;
        }
        wrapped.workOn(p, t);
    }
}
 

One way to do this would be with an explicit wrapper class, as shown above. The BuilderRobot class takes a Robot in its constructor and intercepts the workOn method to make sure the tool used in any projects is of a nondestructive variety. What's more, since the BuilderRobot wrapper implements the Robot interface, you can use a BuilderRobot instance any place you could use a Robot.

The drawback to this wrapper-style BuilderRobot is evident when you modify or expand the Robot interface. Adding a method to the Robot interface means adding a wrapper method to the BuilderRobot class. Adding 10 methods to Robot means adding 10 methods to BuilderRobot. If BuilderRobot, CrusherRobot, SpeedyRobot, and SlowRobot are all Robot wrapper classes, you'll have to add 10 methods to each of them, too. It's a tedious, silly way to handle things.
 
public class BuilderRobot extends MyRobot {
    public void workOn(Project p, Tool t) {
        if (t.isDestructive()) {
            t = Tool.RATCHET;
        }
        super.workOn(p, t);
    }
}
 

The above code shows another way to program a BuilderRobot. Here, we see that BuilderRobot is a subclass of MyRobot. This arrangement fixes the problem encountered in the wrapper solution demonstrated in the second code listing; modifications to the Robot interface do not require modifications to BuilderRobot. However, a new problem has arisen: Only MyRobot objects can be BuilderRobots. Before, any object implementing the Robot interface could become a BuilderRobot. Now, the linear class parentage restrictions imposed by Java prevent us from arranging classes so that an ArbitraryRobot is a BuilderRobot.

A restriction
Dynamic proxies offer the best of both worlds. Using dynamic proxies, you can create wrapper classes that don't require explicit wrappers for all methods, or subclasses without strict parentage—whichever way you prefer to think of it. Dynamic proxies do, however, have a restriction. When you use dynamic proxies, the object being wrapped/extended must implement an interface that defines all the methods that are to be made available in the wrapper. But that restriction, far from being onerous, simply encourages good design. My rule of thumb is that each class should implement at least one (nonconstant) interface. Not only does good interface usage make using dynamic proxies possible, but it also encourages good program modularity in general.

Using dynamic proxies
The code listing shown below demonstrates the class necessary for creating a BuilderRobot using dynamic proxies. Note that the class we've created, BuilderRobotInvocationHandler, doesn't even implement the Robot interface. Instead it implements java.lang.reflect.InvocationHandler, which provides just a single method, invoke. This method acts as a choke point through which any method calls on the proxy object will pass. Looking at the body of invoke, we can see that it checks the name of the method being called, and if it's workOn, the second parameter is switched to a nondestructive tool.

However, we've still just got an InvocationHandler with an invoke method, not the Robot object we're looking for. The creation of the actual Robot instance is where the real magic of dynamic proxies is apparent. At no point in the source code do we define either a Robot wrapper or subclass. Nonetheless, we end up with a dynamically created class implementing the Robot interface and incorporating the Builder tool filter by invoking the code snippet found in the static createBuilderRobot method of the BuilderRobotInvocationHandler.
 
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class BuilderRobotInvocationHandler implements InvocationHandler {
    private Robot wrapped;
    public BuilderRobotInvocationHandler(Robot r) {
        wrapped = r;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
           throws Throwable {
        if ("workOn".equals(method.getName())) {
            args[1] = Tool.RATCHET;
        }
        return method.invoke(wrapped, args);
    }
    public static Robot createBuilderRobot(Robot toWrap) {
        return (Robot)(Proxy.newProxyInstance(Robot.class.getClassLoader(),
            new Class[] {Robot.class},
                new BuilderRobotInvocationHandler(toWrap)));
    }
    public static final void main(String[] args) {
        Robot r = createBuilderRobot(new MyRobot());
        r.workOn("scrap", Tool.CUTTING_TORCH);
    }
}
 

The code in createBuilderRobot may look a little daunting at first, but it just tells the Proxy class to use a specified class loader to dynamically create an object that implements the specified interfaces (in this case, Robot) and to use the provided InvocationHandler in lieu of traditional method bodies. The resulting object returns true in an instanceof Robot test and has all the methods you would expect to find in any class implementing the Robot interface.

Interesting is the complete lack of reference to the Robot interface within the invoke method of the BuilderRobotInvocationHandler class. InvocationHandlers are not specific to the interfaces for which they're providing proxy method implementations, and it's certainly possible to write an InvocationHandler that can serve as the back end for many proxy classes.

In this example, however, we did provide our BuilderRobotInvocationHandler with another instance of the RobotInterface as a constructor parameter. All method calls on the proxy Robot instance are eventually delegated to this "wrapped" Robot by the BuilderRobotInvocationHandler. However, although it is the most common arrangement, it's important to understand that an InvocationHandler need not delegate to another instance of the interface being proxied. An InvocationHandler can be completely capable of providing the method bodies on its own without a delegation target.

Lastly, it should be noted that the invoke method in our BuilderRobotInvocationHandler isn't very resilient to changes in the Robot interface. If the workOn method was renamed, the nondestructive tool trap would silently fail, and we'd have BuilderRobots going about causing damage. More easily detected but nonetheless problematic would be the overloading of the workOn method. Methods with the same name but a different parameter list could result in a ClassCastException or ArrayIndexOutOfBoundsException at run time. The code below shows a fix that yields a more flexible BuilderRobotInvocationHandler. Here, we see that any time a tool is used in any method, it's replaced by a nondestructive tool. Try to do that with subclassing or traditional delegation.
 
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class BuilderRobotInvocationHandler implements InvocationHandler {
    private Robot wrapped;
    public BuilderRobotInvocationHandler(Robot r) {
        wrapped = r;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Class[] paramTypes = method.getParameterTypes();
        for (int i=0; i < paramTypes.length; i++) {
            if (Tool.class.isAssignableFrom(paramTypes[i])) {
                args[i] = Tool.RATCHET;
            }
        }
        return method.invoke(wrapped, args);
    }
    public static Robot createBuilderRobot(Robot toWrap) {
        return (Robot)(Proxy.newProxyInstance(Robot.class.getClassLoader(),
            new Class[] {Robot.class},
                new BuilderRobotInvocationHandler(toWrap)));
    }
    public static final void main(String[] args) {
        Robot r = createBuilderRobot(new MyRobot());
        r.workOn("scrap", Tool.CUTTING_TORCH);
    }
}

Ideas for use
Substituting tools for robots isn't exactly a regular programming chore in most development environments, but there are plenty of other ways to use dynamic proxies. They can provide a debugging layer that effortlessly records the specifics of all method invocations on an object. They can perform bounds checking and validation of method arguments. They can even be used to swap out alternate, local testing back ends on the fly when interfacing with remote data sources. If you use good interface-driven designs, I think you'll find more places to use dynamic proxies than you ever expected and will be pleasantly surprised at how well they fit a multitude of problems.

Editor's Picks

Free Newsletters, In your Inbox