Core Java

Java Dynamic Proxy

Proxy is a design pattern. We create and use proxy objects when we want to add or modify some functionality of an already existing class. The proxy object is used instead of the original one. Usually the proxy objects have the same methods as the original one and in Java proxy classes usually extend the original class. The proxy has a handle to the original object and can call the method on that.

This way proxy classes can implement many things in a convenient way:

  • logging when a method starts and stops
  • perform extra checks on arguments
  • mocking the behavior of the original class
  • implement lazy access to costly resources

without modifying the original code of the class. (The above list is not extensive, only examples.)

In practical applications the proxy class does not directly implement the functionality. Following the single responsibility principle the proxy class does only proxying and the actual behavior modification is implemented in handlers. When the proxy object is invoked instead of the original object the proxy decides if it has to invoke the original method or some handler. The handler may do its task and may also call the original method.

Even though the proxy pattern does not only apply into situation when the proxy object and proxy cass is created during run-time, this is an especially interesting topic in Java. In this article I will focus on these proxies.

This is an advanced topic because it requires the use of the reflection class, or byte code manipulation or compiling Java code generated dynamically. Or all of these. To have a new class not available as a byte code yet during run-time will need the generation of the byte code, and a class loader that loads the byte code. To create the byte code you can use cglib or bytebuddy or the built in Java compiler.

When we think about the proxy classes and the handlers they invoke we can understand why the separation of responsibilities in this case is important. The proxy class is generated during run-time, but the handler invoked by the proxy class can be coded in the normal source code and compiled along the code of the whole program (compile time).

The easiest way to do this is to use the java.lang.reflect.Proxy class, which is part of the JDK. That class can create a proxy class or directly an instance of it. The use of the Java built-in proxy is easy. All you need to do is implement a java.lang.InvocationHandler so that the proxy object can invoke that. InvocationHandler interface is extremely simple. It contains only one method: invoke(). When invoke() is invoked the arguments contain the original object, which is proxied, the method that was invoked (as a reflection Method object) and the object array of the original arguments. A sample code demonstrate the use:

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyDemo {

    interface If {
        void originalMethod(String s);
    }

    static class Original implements If {
        public void originalMethod(String s) {
            System.out.println(s);
        }
    }

    static class Handler implements InvocationHandler {
        private final If original;

        public Handler(If original) {
            this.original = original;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws IllegalAccessException, IllegalArgumentException,
                InvocationTargetException {
            System.out.println("BEFORE");
            method.invoke(original, args);
            System.out.println("AFTER");
            return null;
        }
    }

    public static void main(String[] args){
        Original original = new Original();
        Handler handler = new Handler(original);
        If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),
                new Class[] { If.class },
                handler);
        f.originalMethod("Hallo");
    }

}

If the handler wants to invoke the original method on the original object it has to have access it. This is not provided by the Java proxy implementation. You have to pass this argument to the handler instance yourself in your code. (Note that there is an object usually named proxy passed as an argument to the invocation handler. This is the proxy object that the Java reflection dynamically generate and not the object we want to proxy.) This way you are absolutely free to use a separate handler object for each original class or use some shared object that happens to know some way which original object to invoke if there is any method to invoke at all.

As a special case you can create an invocation handler and a proxy of an interface that does not have any original object. Even more it is not needed to have any class to implement the interface in the source code. The dynamically created proxy class will implement the interface.

What should you do if the class you want to proxy does not implement an interface? In that case you have to use some other proxy implementation. We will look at about that next week.

Reference: Java Dynamic Proxy from our JCG partner Peter Verhas at the Java Deep blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button