Dynamic Proxies implementing Decorators

[Updated: Apr 6, 2017, Created: Mar 7, 2016]

In this example we are going to demonstrate how to use JDK interface based proxies to implement a general purpose decorator.

Assume we want to cache data of some expensive methods calls of multiple objects implementing different interfaces.

We will first show how to apply the decorator pattern without using dynamic proxies.


The Interface

public interface IObject {
    String getData ();
}

The Implementation

public class MyObject implements IObject {

    public String getData () {
        return "expensiveData-" + System.nanoTime();
    }
}

The Decorator without Proxies

package com.logicbig.example;

import java.util.HashMap;
import java.util.Map;

public class NormalCacheDecorator implements IObject {

    private IObject original;
    private Map<String, Object> cacheData = new HashMap<>();

    public NormalCacheDecorator (IObject original) {

        this.original = original;
    }

    @Override
    public String getData () {
        Object data = cacheData.get("getData");
        if (data == null) {
            data = original.getData();
            cacheData.put("getData", data);

        }
        return (String) data;
    }
}

Using the decorator:

 public static void main (String[] args) {
        MyObject object = new MyObject();
        IObject decorator = new NormalCacheDecorator(object);
        System.out.println(decorator.getData());
        System.out.println(decorator.getData());
    }

Output:

expensiveData-1781118984883837
expensiveData-1781118984883837

That works but the problem is: caching data is a general problem. If we have multiple objects implementing different interface, we have to repeat the same decorator logic for each object.

Now we going to see how we can do that generically with JDK dynamic proxies



Creating a Generic Decorator with Dynamic Proxies

public class GenericCacheDecorator implements InvocationHandler {

    private Map<String, Object> cachedData = new HashMap<>();
    private Object EMPTY = new Object();
    private Object obj;

    private GenericCacheDecorator (Object obj) {
        this.obj = obj;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            for (PropertyDescriptor desc : beanInfo.getPropertyDescriptors()) {
                cachedData.put(desc.getReadMethod().getName(), EMPTY);
            }

        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
    }

    public static <I, T extends I> I decorate (T t, Class<I> interfaceClass) {
        GenericCacheDecorator cacheableDecorator = new GenericCacheDecorator(t);
        return (I) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                                          new Class[]{interfaceClass}, cacheableDecorator);

    }

    public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
        if (cachedData.containsKey(method.getName())) {
            Object o = cachedData.get(method.getName());
            if (o == EMPTY) {
                Object returned = method.invoke(obj, args);
                cachedData.put(method.getName(), returned);
                return returned;
            } else {
                return o;
            }
        }
        return method.invoke(args);
    }
}

The above invocation handler implementation is agnostic to any particular interface or object type which can be used to cache data returned by getters on the underlying object.


 public static void main (String[] args) {
        MyObject object = new MyObject();
        IObject iObject = GenericCacheDecorator.decorate(object, IObject.class);
        System.out.println(iObject.getData());
        System.out.println(iObject.getData());
    }

Output:

expensiveData-1783122642531745
expensiveData-1783122642531745

Example Project

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.0.4

Java Dynamic Proxies Decorator Select All Download
  • jdk-dynamic-proxy-decorator
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also