It's all notes of personal learning process, not summary, no reference value, but this book is great
Basic grammar
Definition annotation
Annotations usually contain elements that represent specific values. These values can be used by programs or tools when analyzing and processing annotations. The annotated element looks like an interface method, but you can specify a default value for it.
for example
// annotations/UseCase.java import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { int id(); String description() default "no description"; }
Meta annotation
The element of Retention annotation is very important, and the RUNTIME annotation reminds me of spring annotation development
RUNTIME: the VM will also retain annotations during RUNTIME, so the information of annotations can be read through reflection mechanism.
Write annotation processor
// annotations/UseCaseTracker.java import java.util.*; import java.util.stream.*; import java.lang.reflect.*; public class UseCaseTracker { public static void trackUseCases(List<Integer> useCases, Class<?> cl) { for(Method m : cl.getDeclaredMethods()) { UseCase uc = m.getAnnotation(UseCase.class); if(uc != null) { System.out.println("Found Use Case " + uc.id() + "\n " + uc.description()); useCases.remove(Integer.valueOf(uc.id())); } } useCases.forEach(i -> System.out.println("Missing use case " + i)); } public static void main(String[] args) { List<Integer> useCases = IntStream.range(47, 51) .boxed().collect(Collectors.toList()); trackUseCases(useCases, PasswordUtils.class); } }
It is to get all the methods of the class by using the reflection method, and then get the annotation for the method. I have looked at the source code. The methods for getting the annotation for the method object are getannotation (class < T > Annotation class) gets the annotation of a fixed class, as shown in the above code; getdeclaredanotions() gets all the annotations, and returns the array of annotation class (this seems to be the parent class of all annotations); and getParameterAnnotations() gets the annotation of parameters of this method: returns an array of arrays of annotations that represents the annotations on the formal parameters, in declaration order
Note that useCases.remove(Integer.valueOf(uc.id())); this line uses the method of remove(Object m), that is, to delete the first m object, and useCases.remove(uc.id()) calls remove(int m), that is, to delete the m object.
Annotation element
Default limit
First, an element cannot have an indefinite value. That is, the element either has a default value or provides the value of the element when using annotations.
Generate external file
Object / relationship mapping: it's Mybatis that kind of thing that maps JavaBeans to the database
Alternatives
Annotation does not support inheritance
Implementation processor
// annotations/database/TableCreator.java // Reflection-based annotation processor // {java annotations.database.TableCreator // annotations.database.Member} package annotations.database; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class TableCreator { public static void main(String[] args) throws Exception { if (args.length < 1) { System.out.println( "arguments: annotated classes"); System.exit(0); } for (String className : args) { Class<?> cl = Class.forName(className); DBTable dbTable = cl.getAnnotation(DBTable.class); if (dbTable == null) { System.out.println( "No DBTable annotations in class " + className); continue; } String tableName = dbTable.name(); // If the name is empty, use the Class name: if (tableName.length() < 1) tableName = cl.getName().toUpperCase(); List<String> columnDefs = new ArrayList<>(); for (Field field : cl.getDeclaredFields()) { String columnName = null; Annotation[] anns = field.getDeclaredAnnotations(); if (anns.length < 1) continue; // Not a db table column if (anns[0] instanceof SQLInteger) { SQLInteger sInt = (SQLInteger) anns[0]; // Use field name if name not specified if (sInt.name().length() < 1) columnName = field.getName().toUpperCase(); else columnName = sInt.name(); columnDefs.add(columnName + " INT" + getConstraints(sInt.constraints())); } if (anns[0] instanceof SQLString) { SQLString sString = (SQLString) anns[0]; // Use field name if name not specified. if (sString.name().length() < 1) columnName = field.getName().toUpperCase(); else columnName = sString.name(); columnDefs.add(columnName + " VARCHAR(" + sString.value() + ")" + getConstraints(sString.constraints())); } StringBuilder createCommand = new StringBuilder( "CREATE TABLE " + tableName + "("); for (String columnDef : columnDefs) createCommand.append( "\n " + columnDef + ","); // Remove trailing comma String tableCreate = createCommand.substring( 0, createCommand.length() - 1) + ");"; System.out.println("Table Creation SQL for " + className + " is:\n" + tableCreate); } } } private static String getConstraints(Constraints con) { String constraints = ""; if (!con.allowNull()) constraints += " NOT NULL"; if (con.primaryKey()) constraints += " PRIMARY KEY"; if (con.unique()) constraints += " UNIQUE"; return constraints; } }
This code is very good, but you need to manually pass in the fully qualified class name of the Member as the parameter. Note that the getdeclaredanotions method is used to get the annotation of the field. The book explains that: because there is no inheritance mechanism for the annotation, if you want to get the behavior of approximate multimodality, using getdeclaredanotions() seems to be the only way.
I don't quite understand what it means
Using javac to process annotations
This section mainly deals with how to handle the annotation of @ Retention(RetentionPolicy.SOURCE). After compiling, the annotation information of this annotation element will be lost
Simplest processor
Check the decompiled code of SimpleTest.class as follows, and find that the annotation information has been deleted
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE, ElementType.PACKAGE, ElementType.FIELD, ElementType.LOCAL_VARIABLE}) public @interface Simple { String value() default "-default-"; }
package chapter23.simplest; // annotations/simplest/SimpleProcessor.java // A bare-bones annotation processor import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import java.util.*; @SupportedAnnotationTypes("chapter23.simplest.Simple") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class SimpleProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { for (TypeElement t : annotations) System.out.println(t); for (Element el : env.getElementsAnnotatedWith(Simple.class)) display(el); return false; } private void display(Element el) { System.out.println("==== " + el + " ===="); System.out.println(el.getKind() + " : " + el.getModifiers() + " : " + el.getSimpleName() + " : " + el.asType()); if (el.getKind().equals(ElementKind.CLASS)) { TypeElement te = (TypeElement) el; System.out.println(te.getQualifiedName()); System.out.println(te.getSuperclass()); System.out.println(te.getEnclosedElements()); } if (el.getKind().equals(ElementKind.METHOD)) { ExecutableElement ex = (ExecutableElement) el; System.out.print(ex.getReturnType() + " "); System.out.print(ex.getSimpleName() + "("); System.out.println(ex.getParameters() + ")"); } } }
The book says it needs to be called
Javac - processor annotations. Simple. Simpleprocessor simpletest. Java, the code will print out the results. I tried for half a day, but I didn't print anything. It's empty
@SupportedAnnotationTypes and @ SupportedSourceVersion are respectively used to specify the fully qualified class name and source code version of the annotation
Env.getelementsnannotatedwith (Simple. Class) obtains all elements with Simple annotation and prints various information of these elements
Ai Ai Ai Ai I have transferred it out. I need to compile manually in order. I need to enter the src folder to start compiling. The class file generated by idea is in the out folder, which is separate from the source file. So I can't debug it. First compile the Simple class, because other classes depend on this class, then compile the SimpleTest class, and finally compile the SimpleTest class
The compilation results are as follows
The object of Element type is obtained through env.getelementsnannotatedwith (Simple. Class). I understand that it is the Element with Simple annotation. It may be a method, a class or a member, and then it is processed in the display method
At the same time, the author uses the downward transformation
Element can only perform operations that are common to all basic objects parsed by the compiler, while things like classes and methods have additional information to extract. So (if you read the right document, but I didn't find it in any document - I had to look for clues through StackOverflow) you check which ElementKind it is, and then convert it down to a more specific element type, inject TypeElement for CLASS and ExecutableElement for METHOD. At this point, you can call other methods for these elements.
It seems that the document author missed this step
More complex processors
The function is to extract the public non-static methods in the multiplexer, build a new interface, and specify the name of the generated interface through the @ ExtractInterface annotation
The specific details are explained very well in the book, I won't go into details