Java Reflection Dynamic Proxy Example

0

Proxy is an object that acts on behalf of another object. This is possible if the proxy object supports target object’s type so that the proxy can be substituted for the target wherever the target object type is used.

In order for this to work, the proxy object must implement the same interface as the target object. The proxy object may implement additional interfaces apart from the target object’s interface based on the need.

We also need an interceptor that can intervene a method call and decide how to deal with the call. Since a proxy object is able to intervene a call, it gets a chance to add functionality before or after a method call. Its up to it whether to delegate the call to its target or manage the call itself.

This is a powerful feature as one is able to tweak the behavior of an object without changing the actual object.

In this article, we will see how we can use Java reflection’s Proxy to add serializability to a payload object which is not serialized. You can think of the payload object as an outcome of some third party API whose code is not in your control but still you may want to send the payload object across the wire.

Target Interface

Define the target interface and its implementation.

IPayload:

package com.javarticles.reflection;

public interface IPayload {
    String getContent();
}

Payload:

package com.javarticles.reflection;


public class Payload implements IPayload {
    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
    
    public String toString() {
        return "Payload: " + getContent();
    }
}

Let’s create an instance of the target implementation and try to serialize.

PayloadNotASerializableExample:

package com.javarticles.reflection;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class PayloadNotASerializableExample {

    public static void main(String[] args) throws IOException {
        Payload f= new Payload();        
        f.setContent("Hello");
        System.out.println("Target object: " + f);
        System.out.println("Is target object instance of Serializable: " + (f instanceof Serializable));
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        System.out.println("Serialize "  + f);
        out.writeObject(f);
        out.close();
    }

}

As you can see below, serialization fails with error java.io.NotSerializableException as the target object doesn’t implement Serializable.

Output:

Target object: Payload: Hello
Is target object instance of Serializable: false
Serialize Payload: Hello
Exception in thread "main" java.io.NotSerializableException: com.javarticles.reflection.Payload
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at com.javarticles.reflection.PayloadNotASerializableExample.main(PayloadNotASerializableExample.java:20)

Serialized form of the target object

We will come up with an interface that will be the serialized form of the target object. The actual implementation will be a wrapper around the target object so that it can provide the serialized version of target object. It needs to:

  1. Extend Serializable
  2. Provide a method that returns the actual target object getPayload()

ISerializedPayloadProvider:

package com.javarticles.reflection;

import java.io.Serializable;

public interface ISerializedPayloadProvider extends Serializable {
    IPayload getPayload();
}

Here is the implementation of ISerializedPayloadProvider with the java serializable methods readObject() and writeObject(). The methods in turn will write the payload fields to output stream to serialize and read the fields back during de-serialization.

SerializedPayloadProvider:

package com.javarticles.reflection;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializedPayloadProvider implements ISerializedPayloadProvider {
    private static final long serialVersionUID = 1L;
    private transient IPayload f;

    public SerializedPayloadProvider(IPayload f) {
        this.f = f;
    }

    private void readObject(ObjectInputStream inputStream) throws IOException,
            ClassNotFoundException {
        inputStream.defaultReadObject();
        String content = inputStream.readUTF();
        Payload localF = new Payload();
        localF.setContent(content);
        this.f = localF;
    }

    private void writeObject(ObjectOutputStream outputStream)
            throws IOException, ClassNotFoundException {
        outputStream.defaultWriteObject();
        outputStream.writeUTF(f.getContent());
    }

    @Override
    public IPayload getPayload() {
        return f;
    }
}

Invocation Handler

Next we need an invocation handler which the proxy object will use to handle the method calls invoked on the proxy object.
The invocation handler implements interface InvocationHandler. It contains only one method called invoke().
When a method is invoked on a proxy instance, it forwards method calls to its invocation handler by calling invoke(). Its signature is:

public Object invoke( Object proxy, Method method, Object[] args)
throws Throwable
{
    return method.invoke(target, args);
}

The proxy instance provides a reference to itself, and to a Method object representing the invoked method and apart form then it also provides the original arguments for the method call are passed to invoke as an object array.

PayloadProviderInvocationHandler:

package com.javarticles.reflection;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class PayloadProviderInvocationHandler implements InvocationHandler, Serializable {
    private static final long serialVersionUID = 1L;
    private static final Method TO_STRING_METHOD = getMethod("toString",
            (Class<?>[]) null);
    private ISerializedPayloadProvider fp;

    public PayloadProviderHandler(ISerializedPayloadProvider fp) {
        this.fp = fp;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out
                .println("PayloadProviderInvocationHandler.invoke() called, method to be invoked is "
                        + method.getName());
        Object returnValue;
        try {
            returnValue = method.invoke(this.fp.getPayload(), args);
        } catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
        if (TO_STRING_METHOD.equals(method)) {
            return "Proxy (" + returnValue + ")";
        }
        return returnValue;
    }

    private static Method getMethod(String methodName, Class<?>... paramTypes) {
        try {
            return Object.class.getMethod(methodName, paramTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }

}

Proxy Instance

So how do we create a proxy instance?
The two important entities that the proxy object depends on are:

  1. Class loader
  2. Set of interfaces that you want the proxy object to support for
  3. Invocation handler that intervenes the method calls
  4. The proxy instance is created using the newProxyInstance() factory methods:
    IPayload fProxy = (IPayload) Proxy.newProxyInstance(
                    f.getClass().getClassLoader(), new Class<?>[] {IPayload.class, Serializable.class},
                    new PayloadProviderInvocationHandler(fp));
    

The final class constructed by the factory method is a public final subclass of Proxy. We call the interfaces that the proxy class implements in this way, the proxied interfaces. Since we want the serializability, one of the interface that proxy will implement is Serializable. Th second interface that we want is the target object’s interface, that is, IPayload. This is important then only we will be able to cast the proxy to a target object’s type.

With these changes, when we create a proxy, we can serialize it and de-serialize it without any errors.

ProxySerialzablePayloadExample:

package com.javarticles.reflection;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Proxy;


public class ProxySerialzablePayloadExample {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Payload f= new Payload();
        f.setContent("Hello");
        
        ISerializedPayloadProvider fp = new SerializedPayloadProvider(f);
        IPayload fProxy = (IPayload) Proxy.newProxyInstance(
                f.getClass().getClassLoader(), new Class<?>[] {IPayload.class, Serializable.class},
                new PayloadProviderInvocationHandler(fp));
        
        System.out.println("Is proxy? " + Proxy.isProxyClass(fProxy.getClass()));
        System.out.println("Target object: " + f);
        System.out.println("Is target object instance of Serializable: " + (f instanceof Serializable));        
        
        System.out.println("Proxy object: " + fProxy);
        System.out.println("Is proxy object instance of Serializable: " + (fProxy instanceof Serializable));
        System.out.println(fProxy.getContent());
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        System.out.println("Serialize object: "  + fProxy);
        out.writeObject(fProxy);
        byte[] b = baos.toByteArray();
        out.close();
        
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        ObjectInputStream in = new ObjectInputStream(bais);
        Object obj = in.readObject();
        in.close();
        System.out.println("Deserialized object: " + obj);  
    }

}

Output:

Is proxy? true
Target object: Payload: Hello
Is target object instance of Serializable: false
PayloadProviderInvocationHandler.invoke() called, method to be invoked is toString
Proxy object: Proxy (Payload: Hello)
Is proxy object instance of Serializable: true
PayloadProviderInvocationHandler.invoke() called, method to be invoked is getContent
Hello
PayloadProviderInvocationHandler.invoke() called, method to be invoked is toString
Serialize object: Proxy (Payload: Hello)
PayloadProviderInvocationHandler.invoke() called, method to be invoked is toString
Deserialized object: Proxy (Payload: Hello)

Download the source code

This was an example about java reflection proxy object.

You can download the source code here: javaReflectionDynamicProxyExample.zip
Share.

Comments are closed.