Wir können dies mit Anmerkungen tun!
Um einen Fehler auszulösen Messager
, senden Sie eine Nachricht mit Diagnostic.Kind.ERROR
. Kurzes Beispiel:
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR, "Something happened!", element);
Hier ist eine ziemlich einfache Anmerkung, die ich geschrieben habe, um dies zu testen.
Diese @Marker
Anmerkung gibt an, dass das Ziel eine Markierungsschnittstelle ist:
package marker;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Marker {
}
Und der Anmerkungsprozessor verursacht einen Fehler, wenn dies nicht der Fall ist:
package marker;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import javax.tools.Diagnostic;
import java.util.Set;
@SupportedAnnotationTypes("marker.Marker")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public final class MarkerProcessor extends AbstractProcessor {
private void causeError(String message, Element e) {
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR, message, e);
}
private void causeError(
Element subtype, Element supertype, Element method) {
String message;
if (subtype == supertype) {
message = String.format(
"@Marker target %s declares a method %s",
subtype, method);
} else {
message = String.format(
"@Marker target %s has a superinterface " +
"%s which declares a method %s",
subtype, supertype, method);
}
causeError(message, subtype);
}
@Override
public boolean process(
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
Elements elementUtils = processingEnv.getElementUtils();
boolean processMarker = annotations.contains(
elementUtils.getTypeElement(Marker.class.getName()));
if (!processMarker)
return false;
for (Element e : roundEnv.getElementsAnnotatedWith(Marker.class)) {
ElementKind kind = e.getKind();
if (kind != ElementKind.INTERFACE) {
causeError(String.format(
"target of @Marker %s is not an interface", e), e);
continue;
}
if (kind == ElementKind.ANNOTATION_TYPE) {
causeError(String.format(
"target of @Marker %s is an annotation", e), e);
continue;
}
ensureNoMethodsDeclared(e, e);
}
return true;
}
private void ensureNoMethodsDeclared(
Element subtype, Element supertype) {
TypeElement type = (TypeElement) supertype;
for (Element member : type.getEnclosedElements()) {
if (member.getKind() != ElementKind.METHOD)
continue;
if (member.getModifiers().contains(Modifier.STATIC))
continue;
causeError(subtype, supertype, member);
}
Types typeUtils = processingEnv.getTypeUtils();
for (TypeMirror face : type.getInterfaces()) {
ensureNoMethodsDeclared(subtype, typeUtils.asElement(face));
}
}
}
Dies sind beispielsweise korrekte Verwendungen von @Marker
:
Diese Verwendungen von @Marker
verursachen jedoch einen Compilerfehler:
Hier ist ein Blog-Beitrag, den ich sehr hilfreich fand, um mit dem Thema zu beginnen:
Kleiner Hinweis: Der Kommentator unten weist darauf hin, dass er aufgrund von MarkerProcessor
Referenzen Marker.class
eine Abhängigkeit von der Kompilierungszeit hat. Ich habe das obige Beispiel mit der Annahme geschrieben, dass beide Klassen in dieselbe JAR-Datei (z. B. marker.jar
) gehen würden, aber das ist nicht immer möglich.
Angenommen, es gibt eine Anwendungs-JAR mit den folgenden Klassen:
com.acme.app.Main
com.acme.app.@Ann
com.acme.app.AnnotatedTypeA (uses @Ann)
com.acme.app.AnnotatedTypeB (uses @Ann)
Dann existiert der Prozessor für @Ann
in einer separaten JAR, die beim Kompilieren der Anwendungs-JAR verwendet wird:
com.acme.proc.AnnProcessor (processes @Ann)
In diesem Fall AnnProcessor
wäre es nicht möglich, den Typ von @Ann
direkt zu referenzieren , da dies eine zirkuläre JAR-Abhängigkeit erzeugen würde. Es könnte nur @Ann
mit String
Namen oder TypeElement
/ referenzieren TypeMirror
.