Настройте java compiler для обработки вашего Annotation (Annotation Processing Tool)
1. Что такое Annotation Processing Tool (APT)
Приводится ситуация:
Вы создаете некоторые свои Annotation и используете их в вашем приложении Java. Эти Annotation имеют правила использования данные вами. Вы хотитте, чтобы компилятор Java (Java compiler) оповещал ошибку использования не по правилам если они возникают во время компиляции. И если вы используете Eclipse чтобы написать код, вы хотите чтобы Eclipse оповещал ошибки использования прямо на IDE.
Это вполне выполнимо с APT (Annotation Processing Tool).
Вы создаете некоторые свои Annotation и используете их в вашем приложении Java. Эти Annotation имеют правила использования данные вами. Вы хотитте, чтобы компилятор Java (Java compiler) оповещал ошибку использования не по правилам если они возникают во время компиляции. И если вы используете Eclipse чтобы написать код, вы хотите чтобы Eclipse оповещал ошибки использования прямо на IDE.
Это вполне выполнимо с APT (Annotation Processing Tool).
Определение APT:
APT (Java annotation processing tool) это инструмент, который вы можете использовать для обработки annotation на исходном коде Java. Все что вам нужно это выполнить (implements) процессор Annotation.
- Например:
@PublicFinal это ваш annotation, ваше правило - он может аннотировать только на методе или поле с модификатром, являющимся публичным и финальным. Если использовать неправильно, оповещение отобразится во время компиляции, одновременно выдает оповещение на IDE:
2. Модель примера
Это модель примера, который я представлю в этой статье:
Ваши Annotation:
- @PublicFinal используется только для метода или поля с модификатором (modifier), являющимися публичными и финальными (public và final).
- @Controller использутеся только для клсса, и название класса должно иметь суффикс Controller.
- @Action используется тоько для метода, возвращающего вид String.
Процессоры PublicFinalProcessor, ControllerProcessor, ActionProcesser будут выполнять задание оповещения при неправильном использовании во время компиляции, включая отображение оповещения ошибок на IDE Eclipse.
3. Project APTProcessor
Для начала, создадим 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) {
}
}
}
}
Объявить Service
Создать файл javax.annotation.processing.Processor в папке META-INF/services как в изображении ниже:
javax.annotation.processing.Processor
org.o7planning.aptprocessor.PublicFinalProcessor
org.o7planning.aptprocessor.ActionProcessor
org.o7planning.aptprocessor.ControllerProcessor
Упаковка project APTProcessor в file jar:
Нажмите на правую кнопку мыши на Project и выберите Export:
Export успешно:
4. Project APTTutorial
Создать Project APTTutorial:
Нажать на правую кнопку мыши на APTTutorial и выбрать properties.
Объявить использование библиотеки APTProccessor который вы создали до этого.
Объявить использование вашего Annotation Processor c Compiler.
Объявить местоположение библиотеки Processor:
Вы можете нажать на Advanced.. чтобы увидеть какой Processor уже зарегистрирован с Compiler
Нажмите на OK чтобы завершить:
Создать некоторые классы test используя ваши Annotation и 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";
}
Оповещение ошибки отображенное на 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;
}
}
Java Basic
- Настройте java compiler для обработки вашего Annotation (Annotation Processing Tool)
- Программирование на Java для группы с помощью Eclipse и SVN
- Руководство Java WeakReference
- Руководство Java PhantomReference
- Сжатие и декомпрессия в Java
- Настройка Eclipse для использования JDK вместо JRE
- Методы String.format() и printf() в Java
- Синтаксис и новые функции в Java 8
- Регулярные выражения Java
- Руководство Java Multithreading Programming
- Библиотеки Java JDBC Driver для различных типов баз данных
- Руководство Java JDBC
- Получить значения столбцов, автоматически возрастающих при вставлении (Insert) записи, используя JDBC
- Руководство Java Stream
- Руководство Java Functional Interface
- Введение в Raspberry Pi
- Руководство Java Predicate
- Абстрактный класс и Interface в Java
- Модификатор доступа (Access modifiers) в Java
- Руководство Java Enum
- Руководство Java Annotation
- Сравнение и Сортировка в Java
- Руководство Java String, StringBuffer и StringBuilder
- Обработка исключений Java - Java Exception Handling
- Руководство Java Generics
- Манипулирование файлами и каталогами в Java
- Руководство Java BiPredicate
- Руководство Java Consumer
- Руководство Java BiConsumer
- Что мне нужно для начала работы с Java?
- История Java и разница между Oracle JDK и OpenJDK
- Установить Java в Windows
- Установите Java в Ubuntu
- Установите OpenJDK в Ubuntu
- Установить Eclipse
- Установите Eclipse в Ubuntu
- Быстрое изучение Java для начинающих
- История бит и байтов в информатике
- Типы данных в java
- Битовые операции
- Команда if else в Java
- команды switch в Java
- Циклы в Java
- Массивы (Array) в Java
- JDK Javadoc в формате CHM
- Наследование и полиморфизм в Java
- Руководство Java Function
- Руководство Java BiFunction
- Пример Java encoding и decoding с использованием Apache Base64
- Руководство Java Reflection
- Java Удаленный вызов методов - Java RMI
- Руководство Программирование Java Socket
- Какую платформу я должен выбрать для разработки приложений Java Desktop?
- Руководство Java Commons IO
- Руководство Java Commons Email
- Руководство Java Commons Logging
- Понимание Java System.identityHashCode, Object.hashCode и Object.equals
- Руководство Java SoftReference
- Руководство Java Supplier
- Аспектно-ориентированное программирование Java с помощью AspectJ (AOP)
Show More
- Руководства Java Servlet/JSP
- Руководства Java Collections Framework
- Java API для HTML, XML
- Руководства Java IO
- Руководства Java Date Time
- Руководства Spring Boot
- Руководства Maven
- Руководства Gradle
- Руководства Java Web Services
- Руководства Java SWT
- Руководства JavaFX
- Руководства Oracle Java ADF
- Руководства Struts2 Framework
- Руководства Spring Cloud