28 may 2008

Reflection & Annotations

This post presents a simple (yet powerful) way to combine reflection and annotations to obtain all the methods present in a class' hierarchy marked with a given annotation.

package bar.foo.utils;

public class ReflectionUtils {

  /**
  * Enforce non-instatiability
  */
  private ReflectionUtils {}

  /**
   * Get all methods annotated with the specified annotation
   * searching on the leaf class and all superclasses.
   */
  public static Method[] getAllAnnotatedMethods(
      Class targetClass, Class annotationClass) {
    List list = new ArrayList();

    // traverse inheritance hierarchy
    do {
      Method[] methods = targetClass.getDeclaredMethods();
        for (Method method : methods) {
          if (method.isAnnotationPresent(annotationClass))
     methods.add(method);
        }
        targetClass = targetClass.getSuperclass();
    } while (targetClass != null);

    return (Method[]) list.toArray(new Method[list.size()]);
  }

}


It could be very useful in a variety of scenarios, e.g. auditing: by just annotating the getters in the audited entity, we'll be able to keep track of the values/changes with a few lines of code:

public class Entity extends BaseEntity {
  private Integer id;
  private MyProperty myProperty;
  
  // we don't want to audit id!
  public Integer getId() {return id;}

  // but we do want to audit myProperty
  @Auditable
  public MyProperty getMyProperty {return myProperty;}
}

...

public class AuditManager {
  public void auditEntity(BaseEntity myEntity) {
    Class entityClass = myEntity.getClass();
    Class annotationClass = Auditable.getClass();
    Method[] auditGetters = ReflectionUtils.getAllAnnotatedMethods(
                                      entityClass, annotationClass);
    
    for (Method method : auditGetters) {
      // audit logic here
    }
  }