注解处理
运行时处理
定义一个注解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源文件,然后在将生成的源文件和指定的源文件一起编译。