The Annotation Processor is introduced

Annotations are a new feature introduced in Java1.5, which can be used for code inspection, code generation and other practical functions. For example, @Override, @nonNULL and other annotations can provide code inspection for the compiler and code style inspector. @Autowire, @Component and other functions in the Spring framework can create and inject beans in Spring, and developers can be freed from complicated configuration files. Annotations have become a common feature in today’s Java class libraries. However, these functions of annotations are not only provided by a annotation can be realized, annotations are just a mark, a metadata, or rely on the internal annotation processor to complete, processing time can be divided into pre-compile, compile time, virtual machine startup, virtual machine startup run time and so on. Many frameworks now prefer to handle annotations at compile time because of the run-time overhead of processing annotations through reflection after the virtual machine is started.

The Annotation Processor is a built-in javAC tool for scanning and processing annotations at compile time. To put it simply, during source code compilation, annotations processor can be used to obtain annotations in source files.

Since the annotation handler works at compile time, we can use the annotation handler to do what we need at compile time. The most common usage is to obtain the relevant annotation data during compilation, and then dynamically generate. Java source files (let the machine write the code for us), usually automatically generate some regular repeated code, which solves the problem of manual compilation of repeated code, greatly improving the coding efficiency.

Write your own annotation processor

  • Create a project in AndroidStuido, such as AnnotationProcessorDemo, and then create two Java Library modules (annotations,Processor) respectively.

  • Create a HelloWorld Annotation in the Annotation

      @Retention(RetentionPolicy.SOURCE)
      @Target(ElementType.TYPE)
      public @interface HelloWorld {
      }
    Copy the code
  • Generate the following simple file package com.kevinXie. annotation;

      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
    
      public final class HelloWorld {
        public static void main(String[] args) {
          System.out.println("Hello, JavaPoet!");
        }
      }
    Copy the code
  • Create a HelloWorldProcessor in the Processor and inherit from the AbstractProcessor class, then override the following methods.

    @override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); } / annotation processing method of core * * * * @ param annotations need to be dealt with annotations (equivalent to getSupportedAnnotationTypes returned in the annotation type) * @ param roundEnv If @return returns true, the annotation will not be processed by any other processor. False, on the other hand, if in front of the inside of the processor getSupportedAnnotationTypes * added HelloWorld annotations and process method returns true, then in the back of the processor, Even if you added in the getSupportedAnnotationTypes * HelloWorld annotations, */ @override public Boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return false; } /** * Add the annotation type supported by this annotation handler (you can also add it using the @supportedannotationTypes () annotation) */ @override public Set<String> getSupportedAnnotationTypes() { return SourceVersion.latestSupported(); } /** * Add the version of the source file supported by this annotation handler (you can also add it using the @supportedSourceversion () annotation) */ @override public SourceVersion getSupportedSourceVersion() { HashSet<String> annotations = new HashSet<>(); annotations.add("com.kevinxie.annotation.HelloWorld"); return annotations; }Copy the code
  • Now you can complete the logic of the process

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { TypeElement supportAnnotation = null; Iterator<? extends TypeElement> iterator = annotations.iterator(); // Get the type of annotations if (iterator.hasNext()) {supportAnnotation = iterator.next(); mMessager.printMessage(Diagnostic.Kind.NOTE, supportAnnotation.getQualifiedName().toString()); } if (supportAnnotation == null) { return false; } // make a type check to see if it is the HelloWorld annotation type, Here we only add the HelloWorld annotation support the if (" com. Kevinxie. An annotation. The HelloWorld "equals (supportAnnotation. GetQualifiedName (). The toString ()))  { Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(supportAnnotation); ElementsAnnotatedWith. ForEach (new Consumer Element < > () {@ Override public void the accept Element (Element) {/ / generated file generateFile(); }}); return true; } else { return false; }}Copy the code
  • Complete the file generation code

    private void generateFile1() { try { JavaFileObject javaFileObject = processingEnv.getFiler().createSourceFile("com.kevinxie.gen.simple.HelloWorld"); Writer writer = javaFileObject.openWriter(); writer.write("package com.kevinxie.gen.simple; \n" + "\n" + "import java.lang.String; \n" + "import java.lang.System; \n" + "\n" + "public final class HelloWorld {\n" + " public static void main(String[] args) {\n" + " System.out.println(\"Hello, JavaPoet! \ "); \n" + " }\n" + "}\n"); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); }}Copy the code
  • Then you need to register the written annotation handler with javAC

    1, right-click the main Folder, New -> Folder, create the Resources Folder, and then create two New -> Folder folders: meta-INF, services

    2, in the services folder, New – > File, create a File, javax.mail. The annotation. Processing. The Processor. In the file, the processor’s full name: enter the custom com. Kevinxie. Processor. HelloWorldProcessor input after you type in return.

    In fact, this manual registration process, but also do not need our trouble. Google developed an annotation tool called AutoService that first adds dependencies to the Processor’s build.gradle

    The compile 'com. Google. Auto. Services: auto - service: 1.0 -rc3'Copy the code

    And then we can use it directly on processor code,

      @AutoService(Processor.class)
      public class DbProcessor extends AbstractProcessor{
          .......
    Copy the code

    This annotation tool automatically generated meta-inf/services/javax.mail annotation. Processing. The Processor file, file also contains the Processor’s full name: com.kevinxie.processor.HelloWorldProcessor

    It may surprise you to see that we can also use annotations in our annotation handler code. The annotation processor runs in its own virtual machine JVM; that is, Javac starts a full Java virtual machine to run the annotation processor.

  • Add dependencies to build.gradle in your app

      implementation project(':Annotation')
      annotationProcessor project(':Processor')
    Copy the code
  • Use the HelloWorld comment in the app, and then rebuild the project, and app/build/generated/source/apt/debug can see the generated files below

Beautiful generated code using JavaPoet

  • Start by adding dependencies to the processor’s build.gradle

    Implementation 'com. Squareup: javapoet: 1.11.1'Copy the code
  • Rewrite the generateFile method

      private void generateFile() {
          MethodSpec main = MethodSpec.methodBuilder("main")
                  .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                  .returns(void.class)
                  .addParameter(String[].class, "args")
                  .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                  .build();
    
          TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                  .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                  .addMethod(main)
                  .build();
    
          JavaFile javaFile = JavaFile.builder("com.kevinxie.gen.simple", helloWorld)
                  .build();
          try {
              javaFile.writeTo(processingEnv.getFiler());
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    Copy the code
  • Rebuild project, the effect is the same as before, the use of JavaPoet can go to see the document

The project address is github.com/554512097/A…