Java Annotation Processing

Kürzlich habe ich aus einer Laune heraus den Java6-Annotation Processor angeworfen. Hier steht wie´s funktioniert (was für ein cooles Teil!)

Die Definition einer Annotation ist hierbei denkbar einfach (@interface statt interface),

Methoden können Return-Values von primitives, Strings, Class und Enum haben.

public @interface Expensive {

int amount();
boolean rare();
}

Wo und wann Annotations eingesetzt werden können dürfte mittlerweile bekannt sein. Die Verwendung der soeben deklarierten Annotation könnte wie folgt aussehen (Das JDT greift zu diesem Zeitpunkt schon)

@Expensive(amount=5,rare=true)
public class Mantel implements Artikel{

private String description;
private int prize;

public Mantel(String desc, int prize){
this.description = desc;
this.prize = prize;
}

@Override
public String getDescription() {
return description;
}
@Override
public int getPrize() {
return prize;
}
}

Per Default wird ein Wert, wenn er der einzige ist, mit Value bezeichnet (Convention).

public @interface Artikeltyp {
String value();
}

Das hat den Vorteil, dass man das „=“ vernachlässigen kann.

@Artikeltyp(„Winter“)

Das Annotation-Framework bietet Meta-Annotations

@RetentionPolicy

annotations

@Retention(RetentionPolicy.RUNTIME) heißt, dass die Annotations zur Runtime ausgewertet werden (und auch verfügbar sein sollen)

@Retention(RetentionPolicy.CLASS) heißt, dass die Annotations in der Klasse verfügbar sein sollen, aber nicht zur Laufzeit

@Retention(RetentionPolicy.SOURCE) heißt, dass der Compiler die Annotations rausschmeißt.

annotations2

@Target(ElementType.METHOD) sagtaus, dass nur Methoden annotiert werden können (etc..)

Das ganze kann jetzt ganz einfach über Reflection ausgelesen werden.

Class<?> clazz = Class.forName(„Mantel“);
for(Method m : clazz.getMethods()){
if(m.isAnnotationPresent(Expensive.class)){
System.out.println(„Annotated“);
Expensive annotation = m.getAnnotation(Expensive.class);
System.out.println(annotation.amount());
}
}

Weitere interessante Annotation-Typen sind

@SupportedAnnotationTypes(value = { „de.md.*“ })

@SupportedSourceVersion(SourceVersion.RELEASE_6)

Noch interessanter ist die Verwendung der Klasse Processor.

@SupportedAnnotationTypes({„de.md.annotations.Artikel“})
public class ArtikelProcessor extends AbstractProcessor{

@Override
public boolean process(Set<? extends TypeElement> arg0,
RoundEnvironment env) {
Set set =  env.getRootElements();
return false;
}
}

Der Processor kriegt ein Set vom Typ TypeElement und ein Element vom Typ RoundEnvironment in der Methode process(..)unbenannt-6annots3

Das RoundEnvironment bietet ZUgriff auf den Vorgang es prozessierens und hierfür Methoden wie getElementsAnnotatedWith(..) und processingOver().

Das war eine erste kleine Einführung, mehr zum Thema mit einem sinnvollen Beispiel folgt demnächst.

(ein einfaches Beispiel für die Verwendung eines Processors findet sich z.B. bei Kai Toedter für den OSGI DS Annotation Processor, danke Kai!!)

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
null, null, null);
Iterable<? extends JavaFileObject> compilationUnits1 = fileManager
.getJavaFileObjects(testFileDirectory + filename);
CompilationTask task = compiler.getTask(null, fileManager, null, null,
null, compilationUnits1);
LinkedList<AbstractProcessor> processors = new LinkedList<AbstractProcessor>();

Processor processor = new Processor();
processor.setOutputDir(„OSGI-INF“);
processors.add(processor);
task.setProcessors(processors);
return task.call();

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s