Customize java compiler processing your Annotation (Annotation Processing Tool)
1. What is Annotation Processing Tool (APT)
No ADS
The situation is that:
You create your own Annotations and use them in your Java application. You set using rules for these Annotations. You want the Java Compiler to notify the error of applying the rules wrongly, if any, at the compile-time. If you use Eclipse for writing codes, you want Eclipse will notify the using error right on IDE.
This request is feasible to APT (Annotation Processing Tool).
You create your own Annotations and use them in your Java application. You set using rules for these Annotations. You want the Java Compiler to notify the error of applying the rules wrongly, if any, at the compile-time. If you use Eclipse for writing codes, you want Eclipse will notify the using error right on IDE.
This request is feasible to APT (Annotation Processing Tool).
APT Definition:
Java annotation processing tool is a tool you can use to process annotations in the sourcecode. All you need is to implement an annotation processor.
- For example:
@PublicFinal is your annotation. Your rule is that it only can annotate on the method or field whose modifier is public and final. If you use it wrongly, there will be a notice displayed at the compile-time, and a notice on IDE at once.
2. The model of example
This is the example model I will introduce in this document:
Your Annotation:
- @PublicFinal is used for method or field whose modifiers are public and final.
- @Controller only uses for class, and the name of class must have the Controller suffix.
- @Action only use for method, returning to String.
Processors including PublicFinalProcessor, ControllerProcessor, ActionProcesser are responsible for having notifications if there are errors at the compile-time. It includes the display of notifying errors on IDE Eclipse,too.
3. Project APTProcessor
No ADS
First, create a Project.
- APTProcessor
Action.java
package org.o7planning.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
}
Controller.java
package org.o7planning.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Controller {
}
PublicFinal.java
package org.o7planning.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface PublicFinal {
}
AcctionProccessor.java
package org.o7planning.aptprocessor;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic.Kind;
import org.o7planning.log.DevLog;
// Work with @Action
@SupportedAnnotationTypes({ "org.o7planning.ann.Action" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ActionProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
@Override
public void init(ProcessingEnvironment env) {
filer = env.getFiler();
messager = env.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
DevLog.log("\n\n");
DevLog.log(" ======================================================== ");
DevLog.log("#process(...) in " + this.getClass().getSimpleName());
DevLog.log(" ======================================================== ");
for (TypeElement ann : annotations) {
DevLog.log(" ==> TypeElement ann = " + ann);
List<? extends Element> es = ann.getEnclosedElements();
DevLog.log(" ====> ann.getEnclosedElements() count = " + es.size());
for (Element e : es) {
DevLog.log(" ========> EnclosedElement: " + e);
}
Element enclosingElement = ann.getEnclosingElement();
DevLog.log(" ====> ann.getEnclosingElement() = " + enclosingElement);
ElementKind kind = ann.getKind();
DevLog.log(" ====> ann.getKind() = " + kind);
Set<? extends Element> e2s = env.getElementsAnnotatedWith(ann);
DevLog.log(" ====> env.getElementsAnnotatedWith(ann) count = "
+ e2s.size());
for (Element e2 : e2s) {
DevLog.log(" ========> ElementsAnnotatedWith: " + e2);
DevLog.log(" - Kind : " + e2.getKind());
// @Action use for method only
// notify if misuse
if (e2.getKind() != ElementKind.METHOD) {
DevLog.log(" - Error!!!");
messager.printMessage(Kind.ERROR, "@Action using for method only ", e2);
} else {
// The name of the method is annotated by @Action
String methodName = e2.getSimpleName().toString();
// (ExecutableElement described for method, constructor,...)
ExecutableElement method = (ExecutableElement) e2;
DevLog.log(" - method : " + method);
TypeMirror retType = method.getReturnType();
DevLog.log(" -- method.getReturnType() : " + retType);
// @Action Only used for method returns the String
// Notify if misuse
if (!String.class.getName().equals(retType.toString())) {
DevLog.log(" - Error!!!");
messager.printMessage(Kind.ERROR,
"Method using @Action must return String", e2);
}
}
}
}
return true;
}
}
ControllProcessor.java
package org.o7planning.aptprocessor;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import org.o7planning.log.DevLog;
// Apply for @Controller
@SupportedAnnotationTypes({ "org.o7planning.ann.Controller" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ControllerProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
@Override
public void init(ProcessingEnvironment env) {
filer = env.getFiler();
messager = env.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
DevLog.log("\n\n");
DevLog.log(" ======================================================== ");
DevLog.log("#process(...) in " + this.getClass().getSimpleName());
DevLog.log(" ======================================================== ");
for (TypeElement ann : annotations) {
DevLog.log(" ==> TypeElement ann = " + ann);
//
List<? extends Element> es = ann.getEnclosedElements();
DevLog.log(" ====> ann.getEnclosedElements() count = " + es.size());
for (Element e : es) {
DevLog.log(" ========> EnclosedElement: " + e);
}
Element enclosingElement = ann.getEnclosingElement();
DevLog.log(" ====> ann.getEnclosingElement() = " + enclosingElement);
ElementKind kind = ann.getKind();
DevLog.log(" ====> ann.getKind() = " + kind);
Set<? extends Element> e2s = env.getElementsAnnotatedWith(ann);
DevLog.log(" ====> env.getElementsAnnotatedWith(ann) count = "
+ e2s.size());
for (Element e2 : e2s) {
DevLog.log(" ========> ElementsAnnotatedWith: " + e2);
DevLog.log(" - Kind : " + e2.getKind());
// @Controller only use for Class
// Notify if misuse
if (e2.getKind() != ElementKind.CLASS) {
DevLog.log(" - Error!!!");
messager.printMessage(Kind.ERROR,
"@Controller using for class only ", e2);
} else {
// The name of the class is annotated by @Controller
String className = e2.getSimpleName().toString();
// @Controller using for class with suffix Controller
// Notify if misuse
if (!className.endsWith("Controller")) {
DevLog.log(" - Error!!!");
messager.printMessage(
Kind.ERROR,
"Class using @Controller must have suffix Controller", e2);
}
}
}
}
return true;
}
}
PublicFinalProcessor.java
package org.o7planning.aptprocessor;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import org.o7planning.log.DevLog;
// Apply for @PublicFinal
@SupportedAnnotationTypes(value = { "org.o7planning.ann.PublicFinal" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class PublicFinalProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
@Override
public void init(ProcessingEnvironment env) {
filer = env.getFiler();
messager = env.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
DevLog.log("\n\n");
DevLog.log(" ======================================================== ");
DevLog.log("#process(...) in " + this.getClass().getSimpleName());
DevLog.log(" ======================================================== ");
DevLog.log(" annotations count = " + annotations.size());
for (TypeElement ann : annotations) {
Set<? extends Element> e2s = env.getElementsAnnotatedWith(ann);
for (Element e2 : e2s) {
DevLog.log("- e2 = " + e2);
Set<Modifier> modifiers = e2.getModifiers();
// @PublicFinal only using for public & final
// Notify if misuse
if (!(modifiers.contains(Modifier.FINAL) && modifiers
.contains(Modifier.PUBLIC))) {
DevLog.log("- Error!!!");
messager.printMessage(Kind.ERROR,
"Method/field wasn't public and final", e2);
}
}
}
// All PublicFinal annotations are handled by this Processor.
return true;
}
}
DevLog.java
package org.o7planning.log;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class DevLog {
public static final String LOG_FILE = "C:/APT/log.txt";
public static void log(Object message) {
if (message == null) {
return;
}
// Make sure the path exists.
new File(LOG_FILE).getParentFile().mkdirs();
//
FileWriter writer = null;
try {
writer = new FileWriter(LOG_FILE, true);
writer.append(message.toString());
writer.append("\n");
writer.close();
} catch (IOException e) {
e.printStackTrace();
try {
writer.close();
} catch (IOException e1) {
}
}
}
}
Declare Service
Create file javax.annotation.processing.Processor located in folder META-INF/services as shown below:
javax.annotation.processing.Processor
org.o7planning.aptprocessor.PublicFinalProcessor
org.o7planning.aptprocessor.ActionProcessor
org.o7planning.aptprocessor.ControllerProcessor
Packing APTProcessor project into jar file:
Right-click Project and select Export:
Export success:
4. Project APTTutorial
No ADS
Create Project APTTutorial:
Right-click APTTutorial select property.
Declare use APTProccessor library that you created earlier.
Declare use your Annotation Processor with Compiler.
Declaring Processor library location:
You can click on Advanced... to see Processors registered with Compiler.
Click OK to complete:
Create a test class using your Annotation and Processor:
PublicFinalTest.java
package org.o7planning.tutorial.apttest;
import org.o7planning.ann.PublicFinal;
public class PublicFinalTest {
@PublicFinal
public final static int ABC = 100;
@PublicFinal
private static String MODULE_NAME = "APT";
}
The error message seen on the IDE:
TestActionController_01.java
package org.o7planning.tutorial.apttest;
import org.o7planning.ann.Action;
import org.o7planning.ann.Controller;
@Controller
public class TestActionController_01 {
@Action
public String exit() {
return null;
}
@Action
public void print() {
}
@Action
public int error() {
return 0;
}
}
TestActionController_02.java
package org.o7planning.tutorial.apttest;
import org.o7planning.ann.Controller;
@Controller
public interface TestActionController_02 {
public String close();
}
TestActionController.java
package org.o7planning.tutorial.apttest;
import org.o7planning.ann.Action;
import org.o7planning.ann.Controller;
@Controller
public class TestActionController {
@Action
public String login() {
return null;
}
}
No ADS
Java Basic
- Data Types in java
- Java PhantomReference Tutorial with Examples
- JDK Javadoc in CHM format
- Java Stream Tutorial with Examples
- Java Predicate Tutorial with Examples
- Java BiConsumer Tutorial with Examples
- Arrays in Java
- JDBC Driver Libraries for different types of database in Java
- Abstract class and Interface in Java
- Java Commons Email Tutorial with Examples
- Install Eclipse
- Bitwise Operations
- Install Eclipse on Ubuntu
- Configuring Eclipse to use the JDK instead of JRE
- Java Commons Logging Tutorial with Examples
- Java Enums Tutorial with Examples
- Loops in Java
- Java Regular Expressions Tutorial with Examples
- Install Java on Ubuntu
- Quick Learning Java for beginners
- Install Java on Windows
- Comparing and Sorting in Java
- Inheritance and polymorphism in Java
- Java Consumer Tutorial with Examples
- Java String, StringBuffer and StringBuilder Tutorial with Examples
- Java Exception Handling Tutorial with Examples
- Example of Java encoding and decoding using Apache Base64
- if else statement in java
- Switch Statement in Java
- Java Supplier Tutorial with Examples
- Java Programming for team using Eclipse and SVN
- Java JDBC Tutorial with Examples
- Java remote method invocation - Java RMI Tutorial with Examples
- Java Multithreading Programming Tutorial with Examples
- Customize java compiler processing your Annotation (Annotation Processing Tool)
- What is needed to get started with Java?
- Java Aspect Oriented Programming with AspectJ (AOP)
- Understanding Java System.identityHashCode, Object.hashCode and Object.equals
- Java Compression and Decompression Tutorial with Examples
- Java Reflection Tutorial with Examples
- Install OpenJDK on Ubuntu
- Java String.format() and printf() methods
- History of Java and the difference between Oracle JDK and OpenJDK
- Introduction to the Raspberry Pi
- Java Socket Programming Tutorial with Examples
- Java Generics Tutorial with Examples
- Manipulating files and directories in Java
- Java WeakReference Tutorial with Examples
- Java Commons IO Tutorial with Examples
- History of bits and bytes in computer science
- Which Platform Should You Choose for Developing Java Desktop Applications?
- Java SoftReference Tutorial with Examples
- Syntax and new features in Java 8
- Java Annotations Tutorial with Examples
- Java Function Tutorial with Examples
- Access modifiers in Java
- Java BiFunction Tutorial with Examples
- Get the values of the columns automatically increment when Insert a record using JDBC
- Java Functional Interface Tutorial with Examples
- Java BiPredicate Tutorial with Examples
Show More
- Java Servlet/Jsp Tutorials
- Java Collections Framework Tutorials
- Java API for HTML & XML
- Java IO Tutorials
- Java Date Time Tutorials
- Spring Boot Tutorials
- Maven Tutorials
- Gradle Tutorials
- Java Web Services Tutorials
- Java SWT Tutorials
- JavaFX Tutorials
- Java Oracle ADF Tutorials
- Struts2 Framework Tutorials
- Spring Cloud Tutorials