Skip to main content

Refactor dynaop Factories using dynaop

Posted by crazybob on June 22, 2004 at 12:25 PM PDT

dynaop uses class proxies to apply aspects to plain classes. Unlike dynamic proxies, class proxies do not require an interface. As a tradeoff, the framework must instantiate the object for you. The ProxyFactory.extend() method creates class proxies by extending classes or interfaces using cglib. Repeated calls to ProxyFactory.extend() (especially those that pass the optional constructor arguments) can quickly become a crosscutting concern. Luckily, a simple aspect can relieve a great deal of the strain.

Let's take an example class Foo:

  public static class Foo {
    String s;
    int i;
    public Foo(String s, int i) {
      this.s = s;
      this.i = i;
    }
    ...
  }

We can implement a factory that uses dynaop to create new instances of Foo with aspects:

  public interface Factory {
    Foo newFoo(String s, int i);
  }              
      
  public class FactoryImpl implements Factory {
    public Foo newFoo(String s, int i) {
      // create a new instance of Foo using the default proxy factory
      // and specified constructor arguments.
      return (Foo) ProxyFactory.getInstance().extend(
        Foo.class,
        new Class[] { String.class, int.class },
        new Object[] { s, new Integer(i) }
      );   
    }
  }

Note that every time we add a new type or constructor, we'll have to create another newXxx() method. We'll also have to keep the Class and Object arrays and our factory method signatures in sync with our constructor arguments. We must manually box primitives. We have a crosscutting concern on our hands.

We can use dynaop to refactor the newXxx() method implementations into a single interceptor and do away with factory implementation class altogether (leaving only the Factory interface):

  public class NewInterceptor implements Interceptor {

    public Object intercept(Invocation i) throws Throwable {
      Method m = i.getMethod();
      return ProxyFactory.getInstance().extend(
        m.getReturnType(),
        m.getParameterTypes(),
        i.getArguments()                                                              ); 
    }
  }

In the dynaop BeanShell configuration, apply NewInterceptor to Factory:

  interceptor(Factory.class, ALL_METHODS, new NewInterceptor());

Now we can instantiate Factory using dynaop and create a Foo instance:

  Factory factory = (Factory) ProxyFactory.getInstance().extend(Factory.class);
  Foo foo = factory.newFoo("foo", 3);

As we add new types or modify their constructor arguments, we only need to tweak the Factory interface. We can throw away our class proxy factory implementation class altogether and leave casting, boxing and array headaches behind.