Chúng tôi có thể làm điều này với các chú thích!
Để nêu ra lỗi, hãy sử dụng Messager
để gửi tin nhắn với Diagnostic.Kind.ERROR
. Ví dụ ngắn gọn:
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR, "Something happened!", element);
Đây là một chú thích khá đơn giản mà tôi đã viết chỉ để kiểm tra điều này.
@Marker
Chú thích này cho biết mục tiêu là một giao diện đánh dấu:
package marker;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Marker {
}
Và bộ xử lý chú thích gây ra lỗi nếu nó không phải là:
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));
}
}
}
Ví dụ, đây là những cách sử dụng chính xác của @Marker
:
Nhưng những cách sử dụng @Marker
này sẽ gây ra lỗi trình biên dịch:
Đây là một bài đăng trên blog mà tôi thấy rất hữu ích khi bắt đầu về chủ đề này:
Lưu ý nhỏ: điều mà người bình luận bên dưới chỉ ra là vì MarkerProcessor
tham chiếu Marker.class
nên nó phụ thuộc vào thời gian biên dịch. Tôi đã viết ví dụ trên với giả định rằng cả hai lớp sẽ đi trong cùng một tệp JAR (giả sử,marker.jar
), nhưng điều đó không phải lúc nào cũng có thể.
Ví dụ: giả sử có một JAR ứng dụng với các lớp sau:
com.acme.app.Main
com.acme.app.@Ann
com.acme.app.AnnotatedTypeA (uses @Ann)
com.acme.app.AnnotatedTypeB (uses @Ann)
Sau đó, bộ xử lý cho @Ann
tồn tại trong một JAR riêng biệt, được sử dụng trong khi biên dịch ứng dụng JAR:
com.acme.proc.AnnProcessor (processes @Ann)
Trong trường hợp đó, AnnProcessor
sẽ không thể tham chiếu @Ann
trực tiếp loại của vì nó sẽ tạo ra một phụ thuộc JAR vòng tròn. Nó sẽ chỉ có thể tham chiếu @Ann
bằng String
tên hoặc TypeElement
/ TypeMirror
.