Eugie Eugie - 1 month ago 11
Java Question

Java Annotations and apt (fundamentals)

I'm really rolling up my sleeves and trying to understand Java annotations for the first time, and have read the Sun, Oracle and Wikipedia articles on the subject. They're easy to understand conceptually, but am finding it difficult putting all the pieces of the puzzle together.

The following example is probably terrible engineering, but just humor me (it's an example!).

Let's say I have the following class:


public Widget
{
// ...

public void foo(int cmd)
{
switch(cmd)
{
case 1:
function1();
break;
case 2:
function2();
break;
case 3:
default:
function3();
break;
}
}
}


Now, somewhere else in my project, I have another class, SpaceShuttle, that has a method called blastOff():


public class SpaceShuttle
{
// ...

public void blastOff()
{
// ...
}
}


Now then, I want to configure an annotation called Widgetize so that any methods annotated with @Widgetize will have Widget::foo(int) invoked prior to their own call.


@interface Widgetize
{
int cmd() default 2;
}


So now let's revisit SpaceShuttle:


public class SpaceShuttle
{
// ...

@Widgetize(3)
public void blastOff()
{
// Since we pass a cmd of "3" to @Widgetize,
// Widget::function3() should be invoked, per
// Widget::foo()'s definition.
}
}


Alas, my questions!


  1. I assume that somewhere I need to define an annotation processor; a Java class that will specify what to do when @Widgetize(int) annotations are encountered, yes? Or does this happen in, say, XML config files that get fed into apt (like the way ant reads build.xml files)?

  2. Edit: If I was correct about these annotation processors in question #1 above, then how do I "map"/"register"/make known these processors to the apt?

  3. In buildscripts, is apt typically ran before javac, so that annotation-based changes or code generation takes place prior to the compile? (This is a best practices-type question).



Thanks and I apologize for my code samples, they turned out a lot bulkier than I intended them to (!)

Answer

This sounds more like AOP (Aspect oriented programming) than annotations. The topics are often confused since AOP uses annotations to achieve it's goals. Rather than reinvent AOP from scratch, I would recommend looking up and existing AOP library such as AspectJ.

However, to answer your specific question, there are two possible approaches to achieve your goal.

Runtime Approach

This is the approach typically taken by container frameworks (like Spring). The way it works is that instead of instantiating your classes yourself, you ask a container for an instance of your class.

The container has logic to examine the class for any RuntimeAnnotations (like @Widgetize). The container will then dynamically create a proxy of your class that calls the correct Widgetize method first and then calls the target method.

The container will then return that proxy to the original requester. The requester will still thing he got the class (or interface) that he asked for and be completely unaware of the proxying behavior added by the container.

This is also the behavior used by AspectJ.

Enhancement Approach

This is the approach taken by AspectJ. To be honest, I don't know a lot of the details of how it works. Somehow, AspectJ will scan your class files (the byte code), figure out where the annotations are, and then modify the byte code itself to call the proxy class instead of the actual class.

The benefit of this approach is that you don't need to use a container. The drawback is that you now have to do this enhancement step after you compile your code.