Learning Java Dynamic Proxies – Basics

Java 1.3 introduced java.lang.reflect.Proxy, which allows code to dynamically implement interfaces. This is done by asking Java to create a proxy around an InvocationHandler that you define. Whenever a method on the proxy is invoked, the proxy delegates the invocation to the invocation handler. For example, the call someMethod("arg1", "arg2") is invoked on proxy3 would result in the associated invocation handler being called with invoke(proxy3, "someMethod", new Object[] { "arg1", "arg2" }). I always think of dynamic proxies as the complement to reflection. Reflection allows a single method call (Method.invoke(...)) to be directed to any number of methods. With dynamic proxies, various different methods’ calls converge to the same handler method (InvocationHandler.invoke(...)).

The easiest practical demonstration of a dynamic proxy focuses on hiding implementations from clients. When I say ‘hiding’, I mean preventing clients from downcasting and even preventing them from getting anything useful out of instance.getClass(). Here’s how one would start such a proxy:

    public  T rudimentaryHideImplementation(T implementation) {
        if (null == implementation) {
            return null;
        }
        final ClassLoader loader = implementation.getClass().getClassLoader();
        final Class[] interfaces = getImplementedInterfaces(implementation);
        final InvocationHandler handler = new RudimentaryHandler(implementation);
        return (T) Proxy.newProxyInstance(loader, interfaces, handler);
    }

    private static Class[] getImplementedInterfaces(Object implementation) {
        return implementation.getClass().getInterfaces();
    }

    private static class RudimentaryHandler implements InvocationHandler {
        private final T _implementation;

        RudimentaryHandler(final T implementation) {
            _implementation = implementation;
        }

        public Object invoke(Object proxy,
                             Method method,
                             Object[] objects)
            throws Throwable {
            try {
                return method.invoke(_implementation, objects);
            }
            catch (InvocationTargetException itx) {
                throw itx.getTargetException();
            }
        }
    }

In the above code, getImplementedInterfaces(...) was oversimplified. Specifically, interfaces[] will only contain the interfaces implemented by the instance’s class; it will not contain interfaces imeplemented by its superclasses. Here’s a more correct version:

    private  Class[] getImplementedInterfaces(Object implementation) {
        LinkedHashSet interfaces = new LinkedHashSet();
        for (Class c = implementation.getClass(); c != null; c = c.getSuperclass()) {
            interfaces.addAll(Arrays.asList(c.getInterfaces()));
        }
        return interfaces.toArray(new Class[interfaces.size()]);
    }

Any reflection gurus will tell you that I’ve also oversimplified the code inside the invocation handler. True. I will cover those details in Part 2.

Advertisement
Post a comment or leave a trackback: Trackback URL.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.