Close

Dynamic Proxies implementing Decorators

[Last Updated: May 8, 2018]

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 requirement. If we have multiple objects implementing different interface, we have to repeat the same decorator logic for each object.

Let's see how we can apply caching logic 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 is required to cache data returned by getters of 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
                • GenericCacheDecorator.java

    See Also