Java核心技術之註解處理
阿新 • • 發佈:2018-11-02
註解處理
執行時處理
定義一個註解ToString(註解本質上就是一種介面)
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ToString {
boolean includeName() default true;
}
任意需要格式化列印的類物件可以使用以上註解
@ToString(includeName=false)
public class Point {
@ToString(includeName=false ) private int x;
@ToString(includeName=false) private int y;
…
}
@ToString
public class Rectangle {
@ToString(includeName=false) private Point topLeft;
@ToString private int width;
@ToString private int height;
…
}
使用反射進行註解處理
public class ToStrings {
public static String toString(Object obj) {
if (obj == null) return "null";
Class<?> cl = obj.getClass();
ToString ts = cl.getAnnotation(ToString.class);
if (ts == null) return obj.toString();
StringBuilder result = new StringBuilder();
if (ts.includeName()) result.append(cl.getName());
result .append("[");
boolean first = true;
for (Field f : cl.getDeclaredFields()) {
ts = f.getAnnotation(ToString.class);
if (ts != null) {
if (first) first = false; else result.append(",");
f.setAccessible(true);
if (ts.includeName()) {
result.append(f.getName());
result.append("=");
}
try {
result.append(ToStrings.toString(f.get(obj)));
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
}
}
}
result.append("]");
return result.toString();
}
}
原始碼級的註解處理(程式碼生成)
首先定義一個註解處理器,如下繼承AbstractProcessor
@SupportedAnnotationTypes("com.horstmann.annotations.ToString")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ToStringAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment currentRound) {
if (annotations.size() == 0) return true;
try {
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile("com.horstmann.annotations.ToStrings");
try (PrintWriter out = new PrintWriter(sourceFile.openWriter())) {
out.println("// Automatically generated by com.horstmann.annotations.ToStringAnnotationProcessor");
out.println("package com.horstmann.annotations;");
out.println("public class ToStrings {");
for (Element e : currentRound.getElementsAnnotatedWith(ToString.class)) {
if (e instanceof TypeElement) {
TypeElement te = (TypeElement) e;
writeToStringMethod(out, te);
}
}
out.println(" public static String toString(Object obj) {");
out.println(" return java.util.Objects.toString(obj);");
out.println(" }");
out.println("}");
}
} catch (IOException ex) {
processingEnv.getMessager().printMessage(Kind.ERROR, ex.getMessage());
}
return true;
}
private void writeToStringMethod(PrintWriter out, TypeElement te) {
String className = te.getQualifiedName().toString();
out.println(" public static String toString(" + className + " obj) {");
ToString ann = te.getAnnotation(ToString.class);
out.println(" StringBuilder result = new StringBuilder();");
if (ann.includeName()) out.println(" result.append(\"" + className + "\");");
out.println(" result.append(\"[\");");
boolean first = true;
for (Element c : te.getEnclosedElements()) {
String methodName = c.getSimpleName().toString();
ann = c.getAnnotation(ToString.class);
if (ann != null) {
if (first) first = false; else out.println(" result.append(\",\");");
if (ann.includeName()) {
String fieldName = Introspector.decapitalize(methodName.replaceAll("^(get|is)", ""));
// Turn getWidth into width, isDone into done, getURL into URL
out.println(" result.append(\"" + fieldName + "=" + "\");");
}
out.println(" result.append(toString(obj." + methodName + "()));");
}
}
out.println(" result.append(\"]\");");
out.println(" return result.toString();");
out.println(" }");
}
}
要在編譯時使用該Processor,首先使用javac對其進行編譯
然後再使用編譯好的Processor編譯其他原始檔。javac -processor xxxProcessor xxxSourceFiles
。
在這個過程中Processor會先生成java原始檔,然後在將生成的原始檔和指定的原始檔一起編譯。