0

I have an object model made up of interfaces with getter/setter methods. Implementations of these objects are created using dynamic proxies where values for the fields implied (using JavaBean naming conventions) are stored in a Map.

I'd like to add methods to these interfaces to provide business logic (you know, like a real object model and not just a collection of POJOs).

My first thought was to create abstract classes that implement each interface but only provide implementations of the business methods. Then I would use these implementations in concert with the Map in the InvocationHandler to provide a full implementation of the interface.

Something like:

interface ModelObject extends BaseModel {
    void setFoo(String foo);
    String getFoo();

    void doSomething();
}

public abstract class ModelObjectImpl implements ModelObject {

    @Override
    public void doSomething()
    {
        // Do something
    }
}

public class ModelObjectInvocationHander implements InvocationHandler {

    Map<String, Object> fieldValues; // holds values for implied fields for getter setter
    ModelObject modelObject;         // holds reference to object implementing business methods

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // Get implied field name for method name by removing "get"/"set" and lower casing next letter
        String fieldName = getBeanNameForMethod(method.getName());

        if (fieldValues.containsKey(fieldName)) {
            return fieldValues.get(fieldName);
        }

        // Not a getter/setter so must be a business method.  Delegate to implementation class
        return method.invoke(modelObject, args);
    }
}

Something like this (but obviously more complicated) would work except that I cannot create an instance of the abstract class. I could make BusinessObjectImpl non-abstract and add do-nothing implementations of the getter/setter methods that would never be called, but that just uglies up the code and causes maintenance issues. I could also have BusinessObjectImpl not actually implement the BusinessObject interface but that breaks the nice binding between implementation and interface leading to errors when the interface and "implementation" get out of sync.

Are there any sneaky Java Reflection tricks I can use to invoke these business methods?

UPDATE: Went with a combination of the Java dynamic proxy framework that's already in place and Javassist to create proxies for the abstract implementation classes. This allows there to be no changes at all to the existing model interfaces until business methods are added on an as-needed basis. The capability is now in place to add behavior to the objects. It's up the developers to start writing true object oriented code now.

public class ModelObjectInvocationHandler implements InvocationHandler
{

    public ModelObjectInvocationHandler(Class<ModelImplementation<? extends BaseModel>> implementationClass)
    {
        if (implementationClass != null) 
        {
            ProxyFactory factory = new ProxyFactory();
            factory.setSuperclass(implementationClass);
            try
            {
                modelObject = (ModelObject) factory.create(new Class<?>[0], new Object[0]);
            }
            catch (Exception e)
            {
                // Exception handling
            }
        }
    }

    Map<String, Object> fieldValues; // holds values for implied fields for getter setter
    ModelObject modelObject;         // holds reference to object implementing business methods

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {

        // Get implied field name for method name by removing "get"/"set" and lower casing next letter
        String fieldName = getBeanNameForMethod(method.getName());

        if (fieldValues.containsKey(fieldName))
        {
            return fieldValues.get(fieldName);
        }

        // Not a getter/setter so must be a business method.  Delegate to implementation class
        if (modelObject != null)
        { 
            return method.invoke(modelObject, args);
        }

        return null;
    }
}

At runtime, I scan for implementation classes and create a Map<Class<? extends BaseModel>, Class<ModelImplementation>>. When creating the dynamic proxy for the interface, I find its implementation class in the map and pass it to the InvocationHandler. Any method that is not matched as a bean name is delegated to the proxy for the implementation class. Of course, it's a little more complicated than that since I have to account for class hierarchies and multiple inheritance within the model interfaces, but the theory is sound.

DanN
  • 1
  • 1
  • 2

2 Answers2

0

I'm not aware of any standard Java reflection tricks that would do that. You could dynamically extend the abstract classes using cglib or javaassist at class load-time. This would improve performance a little bit, because no Proxy object is necessary anymore. Instead you can implement the getter/setter methods directly when creating the new class.

A third way, without those tricks, would be with the delegation pattern:

public class ModelObjectImpl implements ModelObject {
  private final ModelObject delegate;

  public ModelObjectImpl(ModelObject delegate) { 
    this.delegate = delegate;
  }

  @Override
  public void doSomething() { /* Do something */ }

  @Override
  public String getFoo() { return delegate.getFoo(); }

  @Override
  public void setFoo(String foo) { delegate.setFoo(foo); }
}

Feed your proxy, implementing the getter/setter methods of the interface, to the constructor delegate argument. However, while this looks better than stub methods (at least for me) it's still duplicate code. So if you really want to have such dynamic classes, go with dynamic bytecode generation.

References:

nif
  • 3,342
  • 20
  • 18
  • Agree. I'm working on a solution using Javassist based on [prior question / answer](http://stackoverflow.com/questions/3291637/alternatives-to-java-lang-reflect-proxy-for-creating-proxies-of-abstract-classes) – DanN Jul 13 '13 at 23:49
0

One way to do that is to define all your "additional" or "business" method's contract in a new interface, like:

interface ModelObjectExtension {
    void doSomething();
}

interface ModelObject extends ModelObjectExtension {
    void setFoo(String foo);
    String getFoo();
}

public abstract class ModelObjectExtensionImpl implements ModelObjectExtension {

    @Override
    public void doSomething()
    {
        // Do something
    }
}

public class ModelObjectImpl extends ModelObjectExtension { 
    // whatever your current implementation is...
}

and finally you can use following in your handler to call extension methods:

((ModelObjectExtension) modelObject).doSomething();
  • Interesting, but now you need to look in two places to find the methods for a business object. Also, you cannot have ModelObjectExtensionImpl be `abstract` for the same reasons I give above. Of course, in your example is doesn't actually need to be abstract. – DanN Jul 13 '13 at 23:53