Advanced AOP with Guice Type Listeners


There are cross-cutting concerns, or aspects, in any non-trivial program. These blocks of code tend to be repetitive, unrelated to business logic, and don’t lend themselves to being factored out. If you have ever added the same statement at the start of several methods, you have encountered an aspect. For instance, audit, instrumentation, authentication, authorisation could all be considered aspects. If you’d use a sledgehammer to crack a walnut, Spring can help you with AOP by using proxies. Guice can also perform AOP out of the box allowing you to bind implementations of MethodInterceptor. In fact, tutorials were being written about doing that before I wrote my first line of Java. However, it gets more complicated when you need a separate (potentially stateful) interceptor per usage of an annotation, making it infeasible to bind the interceptor statically. If only you could bind the interceptor dynamically, when the intercepted type is first requested, it would be so easy to do. This is exactly what the interface TypeListener allows.

TypeListener is a simple interface with a single method

  <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter);

This method gets invoked the first time a type requested for injection is encountered. At this point you can introspect the TypeLiteral and bind a new MethodInterceptor instance to the TypeEncounter. The mechanics of detecting and binding requested interception is common, so factor it out into a base listener class, deferring creation of the MethodInterceptor until later.

abstract class MethodInterceptorBinder implements TypeListener {

    @Override
    public <T> void hear(TypeLiteral<T> literal, TypeEncounter<T> encounter) {
        Arrays.stream(literal.getRawType().getDeclaredMethods())
              .filter(m -> !m.isSynthetic())
              .forEach(m -> bindInterceptor(m, encounter));
    }

    private void bindInterceptor(Method method, TypeEncounter<?> encounter) {
        final MethodInterceptor interceptor = getInterceptor(method);
        if (interceptor != null) {
            encounter.bindInterceptor(Matchers.only(method), interceptor);
        }
    }

    protected abstract MethodInterceptor getInterceptor(Method method);
}

Suppose we would like to audit calls to a method, associating an audit topic with each method. Then we can just extend MethodInterceptorBinder as below, and bind the listener in a module somewhere. Every method annotated for audit will be audited, and audited separately.

public class AuditBinder extends MethodInterceptorBinder {

  private final Auditor auditor;

  public AuditBinder(Auditor auditor) {
      this.auditor = auditor;
  }

  @Override
  protected MethodInterceptor getInterceptor(Method method) {
      Audited audited = method.getAnnotation(Audited.class);
      return null != audited ?
             new AuditingInterceptor(auditor, audited.topic()) :
             null;
  }
}

public class AuditModule extends AbstractModule {

  private final Auditor auditor;

  public AuditModule(Auditor auditor) {
    this.auditor = auditor;
  }

  @Override
  protected void configure() {
    bindListener(Matchers.any(), new AuditBinder(auditor));
  }
}

One thought on “Advanced AOP with Guice Type Listeners

  1. […] Dropwizard metrics allows you to create application metrics as an aspect of your application quickly. An application instrumented  with Dropwizard consists of a MetricRegistry – basically an in memory key-value store of the state of named metrics – and one or more Reporters. There are several built in reporters including ConsoleReporter, CsvReporter, GangliaReporter and GraphiteReporter (the Ganglia and Graphite reporters require that you are actually running these services). An unofficial reporter designed for Ambari Metrics is hosted here.  Nobody really wants to work with JMX anymore, but, just in case you’re working with prehistoric code, there is also a JMXReporter available out of the box. Reporters are very loosely coupled with instrumentation cut points throughout your code, so it’s very easy to change a reporting strategy. Instrumenting an application manually is extremely simple – you just can’t go wrong following the getting started page – and there are several annotation processing mechanisms for instrumenting methods; for instance there are numerous integrations to be found on Github for frameworks like Spring. Indeed, I wrote my own annotation binding using Guice type listeners on a recent project, which was certainly easy enough (using techniques in this post on type listeners). […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s