Hi, my name is Animesh Kumar. I am a technologist and work in the research lab of a medium scale, innovative, product engineering/outsourcing company, headquartered in India. My primary area of interest is search, high performance computing, distributed computing, web and collaborative machine learning systems. I am also excited about product building and engineering. In my previous avatar, I was a Software Architect and dealt with language solutions and built world’s first Hindi portal, world’s first Hindi TTS, world’s first lingual WAP solution and world’s first light weight embeddable lingual input-output control. My most important work focused on technologies that connect people with low or nil English literacy with the mainstream internet. Mywebdunia was one such medium that let people come forward and speak and share with each other without any language barrier. My life will be spent using the smartness of engineering to affect and empower lives. A good software is a piece of art. It doesn’t intrude, it assists. It empowers. Helps us do more, better. It is beautiful and makes us feel confident about human intellect, and the endless possibilities. I am passionate about ideas, people, behavior and change. I love art, literature, poetry, technology and development. I watch TED. I read Dr. Dobb’s Journal and The New Yorker. I love to read literature, especially by Coetzee, Kundera, Rushdie, Pamuk and Enright. I’m a big fan of a crazy theoretician, freaky thinker, sane poetess, and the most admired product manager. Whenever I get time and inspiration, I write poetry, haiku and short fiction. You might want to read some facts about me. http://anismiles.wordpress.com/me/ Animesh has posted 2 posts at DZone. View Full User Profile

Discovering Java Annotations

07.27.2010
| 19428 views |
  • submit to reddit
Annotations are proving to be widely popular these days. Many annotation-based frameworks (Spring, JPA, Tapestry to name few) are seeing the light of the day, and even small scale projects are using annotation based meta-programming for better separation of concerns. Annotations are wonderful and are sure to stay here.

However, in order to use this, we must be able to locate classes annotated with the Annotations that concern us. So, how can you do it?

First, you find resources where we can look for annotated classes. You can look into “java.class.path” system property (also known as CLASSPATH), or use Classloader or ServletContexts to get a list of Resources. Now, you scan through each resource and check for the annotations that concern you.

The easiest way to scan through a resource is to load it through a Classloader and use the Java Reflection API to look for the specified annotation. However, this approach will only help you to find annotations that are visible at runtime, and loading each resource into memory will consume an unnecessary amount of memory. Otherwise, you can use the ASM or Javassist byte processing libraries. These libraries process byte code and can see runtime annotations. And since they don’t load your resources into memory, they have a low footprint.

Well, to help you here, we have written a small library called Annovention using Javassist. The idea behind Annovention is to help you quickly locate annotated classes, fields or methods. Once you have the pertinent classes you can run your domain logic or do whatever you need to do.

Download Annovention here: http://code.google.com/p/annovention/

Read the detailed blog here:  http://anismiles.wordpress.com/2010/07/26/discovering-java-annotations/

How it works?

Annovention works on a subscribe-and-listen pattern.

  1. You create annotation discovery listeners.
    1. Annovetion supports Class, Field and Method level annotation discovery and each of these listeners must implement ClassAnnotationDiscoveryListener, FieldAnnotationDiscoveryListener or MethodAnnotationDiscoveryListener interfaces respectively.
    2. Each listener has a method supportedAnnotations() that returns an Array of Annotation names. For example, to listen to @Entity and @EntityListeners annotations, the array will be:
      new String[] {Entity.class.getName(), EntityListeners.class.getName()}
    3. Each listener has a method discovered() that is called each time a relevant annotation is located with Class-name, Field-name, Method-name and discovered Annotation-name.
    4. Please note that your listeners are receiving only names of classes, fields and methods. You must use Java Reflection to instantiate them. Remember Class.forName()?
  2. Now, there is a Discoverer class that performs all of the discoveries. It needs Resources and Listeners. Annovention comes with an implementation of ClasspathDiscoverer. This uses “java.class.path” system property and builds an array of resources to scan. You need to register your Listeners to the Discoverer class in order to get notified.

Sample Use

public class SampleAnnotationDiscoverer {

public static void main (String args []) {
// Get a classpath discoverer instance
Discoverer discoverer = new ClasspathDiscoverer();

// Register class annotation listener
discoverer.addAnnotationListener(new MyClassAnnotationListener());
// Register field annotation listener
discoverer.addAnnotationListener(new MyFieldAnnotationListener());
// Register method annotation listener
discoverer.addAnnotationListener(new MyMethodAnnotationListener());

// Fire it
discoverer.discover();
}

/** Dummy ClassAnnotation listener */
static class MyClassAnnotationListener implements ClassAnnotationDiscoveryListener {
private static Log log =
LogFactory.getLog(MyClassAnnotationListener.class);

@Override
public void discovered(String clazz, String annotation) {
log.info("Discovered Class(" + clazz + ") " +
"with Annotation(" + annotation + ")");
}

@Override
public String[] supportedAnnotations() {
// Listens for @Entity and @EntityListeners annotations.
return new String[] {
Entity.class.getName(),
EntityListeners.class.getName()};
}
}

/** Dummy FieldAnnotation listener */
static class MyFieldAnnotationListener implements FieldAnnotationDiscoveryListener {
private static Log log =
LogFactory.getLog(MyFieldAnnotationListener.class);

@Override
public void discovered(String clazz, String field, String annotation) {
log.info("Discovered Field(" + clazz + "." + field + ") " +
"with Annotation(" + annotation + ")");
}

@Override
public String[] supportedAnnotations() {
// Listens for @Id and @Column annotations.
return new String[] {
Id.class.getName(),
Column.class.getName()};
}
}

/** Dummy FieldAnnotation listener */
static class MyMethodAnnotationListener implements MethodAnnotationDiscoveryListener {

private static Log log =
LogFactory.getLog(MyMethodAnnotationListener.class);

@Override
public void discovered(String clazz, String method, String annotation) {
log.info("Discovered Method(" + clazz + "." + method + ") " +
"with Annotation(" + annotation + ")");
}

@Override
public String[] supportedAnnotations() {
// Listens for @PrePersist, @PreRemove and @PostPersist annotations.
return new String[] {
PrePersist.class.getName(),
PostPersist.class.getName(),
PreRemove.class.getName()};
}
}
}

How to extend?

You just have to play with the Discoverer and Filter classes. The general flow is:

  1. Discoverer invokes the findResources() method which returns an array of URLs and then
  2. It iterates through each URL and apply filter, and
  3. Then scan the resource for annotations and
  4. If a valid annotation is found, corresponding listeners are intimated.

You can write your own Filter implementation or use the FilterImpl class that comes with Annovention. Then, just extend the Discoverer class and implement the findResources() method in whatever way you see fit. Then just invoke the discover() method. Easy?

Published at DZone with permission of its author, Animesh Kumar.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Wujek Srujek replied on Tue, 2010/07/27 - 6:51am

I have been working with annotations very recently, and I will definitely try this lib. Looks promising. Thanks for sharing.

Chad Hahn replied on Tue, 2010/07/27 - 7:35am

How does this differ from relections - http://code.google.com/p/reflections/ ?

Tilak Sharma replied on Wed, 2010/07/28 - 1:32am

I had to identify JAXWS or JAXRS annotatted POJOs inside a given Jar file. I achieved that using JDT's IClassFileReader. I have compiled my approach in my blog: http://techie-lucky.blogspot.com/2010/03/parsing-jar-for-annotated-pojos.html. Please do read it and let me know your comments.

However I am able to identify only class level annotations as of now. Will look out for this Annovention library for sure. 

Thanks !

Josh Marotti replied on Wed, 2010/07/28 - 10:32am

Yes, annontations are great, but they also have a downside that you even explain, but don't really point out.  You are coupling your code with the annotation class.  So if you anotate a domain object with hibernate anotations, I can't use that domain UNLESS I put hibernate in as well.  Please be sure you temper all talk of anotations with this point.

cyrille martraire replied on Thu, 2010/07/29 - 2:18pm

 Hi,

 Thanks for sharing that small API, a lightweight solution to discover annotations is welcome, gonna give it a try.

 @Chad -Reflection is more powerful and goes further into indexing the annotated sites into a repository, which you may or may not like for some cases (unless you begin to extend it). Other obvious alternatives are the Annotation Processing Tool in Java 5 and the Pluggable Annotation Processing in Java 6.

 @Josh -I completely agree with you: I regret not to hear more complaints or warning on this matter of coupling which is serious enough; I used to write a post on this topic: http://cyrille.martraire.com/2009/12/principles-for-using-annotations/

And here is another interesting discussion about these concerns on dZone, if you want to feel less alone: http://java.dzone.com/articles/when-good-annotations-go-bad

Cheers

Irek IrekM replied on Fri, 2010/07/30 - 1:00am in response to: Chad Hahn

Spring Framework also has quite extensive framework for scanning annotations, without doing actual classloading. 1:0 for Spring

Scanning all classpath for annotations may be slow - especially painful for desktop appz or applets. Reflections project additionally has a way to find and index annotations at compilation time. Annotation discovering at runtime is then very fast. 1:0 for Reflections

It's hard to say what's better: Reflections or the discovery mechanism from Spring - probably depends on use. But anyway, Annovention is just an unnecessary library - nothing new is brought.

People: Google a bit before writing an own library. Lots of useful things is already done. :-)

Animesh Kumar replied on Mon, 2010/08/02 - 8:34am

Hello Everybody, There are certainly other ways to do things that you can do with Annovention library. What differs here is the approach, that is: Annovention works on subscriber-listener pattern. You hook into the discovery process and get informed about the annotated classes/fields/methods incrementally. Compare it with Spring's Bean-Post-Processor stuff, only that Annovention doesn't ask for Spring libraries. Reflections project is quite ambitious, and work on a different approach. It scans everything and creates a memory hash of it's discovery which you can look into later. Other thing is, Annovention is scalable. If you just want to scan a particular package, you can extend Discoverer class to have this functionality. So, in a sense, you can limit where and how Annovention looks for stuffs, and optimize your discovery performance. Also, Annovention don't load classes with Classloaders, it uses Javassist library to read the bytecodes of a class to pick annotations. So don't worry about the Java Resources. I hope, I've cleared some doubts about Annovention. And whether Annovention is needed or not, I would better leave this on your judgement. wishes, Animesh

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.