View
525
Download
2
Category
Preview:
Citation preview
Java Annotation ProcessingA Beginner Walkthrough
Mahfuz Islam Bhuiyan
Software Engineer, Genweb2
What’s Annotation?● Provide meta-data for Java code
● Can describe usage of an element, e.g. @Deprecated
● Can describe the nature of an element, e.g. @WebService
● and many more
Built-in Java Annotations@Deprecated
@Override
@SuppressWarning
Custom Annotationpublic @interface MyAnnotation { // @interface tells Java that it’s a custom annotation
String value();
String name(); int age(); String[] newNames();}
Custom Annotation@MyAnnotation( value="Test123456", name=" Oveget ", age=37, newNames={"Rishi", "Farzana"})public class MyClass {
}
Custom Annotationpublic @interface MyAnnotation {
String value() default "";
String name() default " The Sufi ";
int age(); String[] newNames();
}
Annotation Processing Trends● Remove boilerplate
● Inject Source Code
● Validate Fields, Methods, Class etc
Annotation Processing Facts● Part of javac
● Introduced in Java 5
● Run at Compile Time(!)
● Own JVM
● Native Java code
● No Reflection(by default)
Java Reflection● Makes it possible to inspect classes, interfaces, fields and methods at runtime,
without knowing the names of the classes, methods etc. at compile time.
● Can instantiate new objects, invoke methods and get/set field values using
reflection.
Java ReflectionsMethod[] methods = MyObject.class.getMethods();
for(Method method : methods){ System.out.println("method = " + method.getName());}
Java ReflectionsClass aClass = MyObject.classField field = aClass.getField("someField");
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objetInstance, value);
@Retention@Retention(RetentionPolicy.RUNTIME) // Allows the annotation to be available at Runtime
@Target({ElementType.METHOD})public @interface MyAnnotation { // Yes, we can apply annotation over another annotation
String value() default "";
}
Annotation Processing Limitations● Generate only new files
● Can’t manipulate already existing files(But byte manipulation possible with
sacrificing debugging capability)
Create Your Own Annotation Processor1. Extends AbstractProcessor
2. Register the processor with javac
Extending Abstract Processorpublic class MyProcessor extends AbstractProcessor {
@Override public synchronized void init(ProcessingEnvironment env){}
@Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
@Override public Set<String> getSupportedAnnotationTypes() { }
@Override public SourceVersion getSupportedSourceVersion() { }
}
Extending Abstract Processor@SupportedSourceVersion(SourceVersion.latestSupported())@SupportedAnnotationTypes({ // Set of full qualified annotation type names })public class MyProcessor extends AbstractProcessor {
@Override public synchronized void init(ProcessingEnvironment env){ }
@Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }}
Extending Abstract Processor@SupportedSourceVersion(SourceVersion.latestSupported())@SupportedAnnotationTypes({ // Set of full qualified annotation type names })public class MyProcessor extends AbstractProcessor {
@Override public synchronized void init(ProcessingEnvironment env){ }
// We need to get our hands dirty with following method @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }}
Register the Processor1. Create a .jar file with our Annotation Processor Java file
2. Provide a special file called javax.annotation.processing.Processor located in
META-INF/services in your .jar file
3. Within the javax.annotation.processing.Processor , there should be the fully
qualified names of the processors contained in the Jar file(like, com.example.
MyProcessor.class)
So, it’ll look likeMyProcessor.jar
=> com
=> example
=> MyProcessor.class
=> META-INF
=> services
=> javax.annotation.processing.Processor
Validate a Class with Custom Annotation Processor
Annotationimport com.example;
public @interface ConstructorCheck{
}
Annotation Processor@SupportedSourceVersion(SourceVersion.latestSupported())@SupportedAnnotationTypes({"com.example.ConstructorCheck"})}public class MyProcessor extends AbstractProcessor {
@Override public synchronized void init(ProcessingEnvironment env){ }
@Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }}
initProcessingEnvironment processingEnv;
@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.processingEnv = processingEnv;}
ProcessingEnvironment● getElementUtils => Elements
● getTypeUtils => Types
● getFiler => Filer
● getMessager => Messager
Element● Is not a class per se !
● Contains value that can be of Class, Interface etc
Elementpublic class User{ // TypeElement
private String name; // VariableElement
private Person personObj; // VariableElement
public User(){} // ExecutableElement
public boolean isUserHasNID(){ // ExecutableElement /*...*/ }}
TypeMirror● Provide some meta data about Element
● Get to know the class inheritance hierarchy, for instance
process@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { checkEntityAnnotatedElements(roundEnv); return false; // true if we want to stop processor’s further execution
}
private void checkEntityAnnotatedElements(RoundEnvironment roundEnv) {
Set <? extends Element > entityAnnotated = roundEnv.getElementsAnnotatedWith(CheckConstructor.class);
for (TypeElement typeElement: ElementFilter.typesIn(entityAnnotated)) {
for (ExecutableElement constructor: ElementFilter.constructorsIn(typeElement.getEnclosedElements())) { List <? extends VariableElement > parameters = constructor.getParameters();
if (parameters.isEmpty()) return; } AnnotationMirror entityAnnotation = getAnnotation(typeElement, entityType.type); processingEnv.getMessager().printMessage(Kind.ERROR, "missing no argument constructor", typeElement, entityAnnotation); }}
Automatic Source Code Generating Tool
JavaPoet● JavaPoet is a API for generating java source files.
● It can be useful when doing things such as annotation processing or interacting
with metadata files.
JavaPoetHere goes a plain simple java class
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); }}
JavaPoetWith JavaPoet, it’ll look like this.
MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class).addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!").build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(main).build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld).build();
javaFile.writeTo(System.out)
Some Cool Annotation Based Tools
Project Lombok● Project Lombok greatly reduces the number of lines of boilerplate code
Dagger 2 ● Dagger 2 is a fork from Dagger 1 under heavy development by Google
● Dependency Injection design pattern without the burden of writing the
boilerplate
● No reflection at all
● Achieved 13% performance boost over Dagger 1
Butter Knife● Butter Knife injects views on Android
● Reduce boilerplate codes
● Support Resource and Event binding too
Butter Knifeclass ExampleActivity extends Activity { TextView title, subtitle; EditText inputTitle, inputSubTitle;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity);
title = (TextView)findViewById(R.id.title); subtitle = (TextView)findViewById(R.id.subtitle); inputTitle = (EditText)findViewById(R.id.inputTitle); inputSubtitle = (EditText)findViewById(R.id.inputSubTitle); }}
Butter Knifeclass ExampleActivity extends Activity { @Bind(R.id.title) TextView title; @Bind(R.id.subtitle) TextView subtitle; @Bind(R.id.subtitle) EditText inputTitle; @Bind(R.id.subtitle) EditText inputSubtitle;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); }}
References● Annotation Processing 101
● Code Generation Using Annotation Processor in Java
● Writing Annotation Processors to aid your development process
● Android Tech Talk: Annotation Processing Boilerplate Destruction
● Java Annotation Processor Tutorial
● Java Annotations
● Official Java Annotation Processor Documentation
@thankYou
Recommended