Detailed review

In the last post, we covered the basics of annotations, how to define annotations, suggestive annotations, and how to write and use them at runtime, using mind maps. Students who have not read the first one and are relatively unfamiliar with the knowledge of annotations are advised to eat the first one first. This article will focus on compile-time annotations and automatic generation of Java files. Chapter 1 Portal:

Android Annotation- Make your code more elegant

This food route

As usual, here is the learning map of this article. It is convenient for you to grasp the study outline. As usual, this chapter will start with some basic classes and methods for handling compile-time annotations, and then go through some concrete examples to learn how to use compile-time annotations to implement some of the convenience features. The edible time of this piece may be a little longer, it is recommended to eat slowly after storing.

Compile time static processing – Be a Java poet

JavaPoet profile

JavaPoet is square’s open source library. See portal below. As the name suggests, JavaPoet, or JavaPoet, is a library that generates Java files through annotations. We can use annotations to generate some repetitive template code using JavaPoet. Thus greatly improve our programming efficiency. ButterKnife, as we know it, simplifies code writing in this way. There are also some Java apis that you need to use when using JavaPoet, which we’ll cover in article 1.

Github-JavaPoet

When used, it is ok to introduce dependencies:

compile 'com. Squareup: javapoet: 1.7.0'
Copy the code

Structured Java files through the eyes of a poet

Those of you who understand compilation know that a code file is really structured data written in a certain syntax. When the compiler processes Java files, it also analyzes the structured data of Java files according to the established syntax. Structured data is the basic element we use to write Java files every day. For example, the basic elements of code include packages, classes, functions, fields, variables, etc. The JDK defines a base class for these elements, namely Element. We use Element and its subclasses to represent these basic elements, which have five subclasses:

The name of the class Elements of expression
PackageElement Represents a package element that can get package names, etc
TypeElement Represents a class or interface program element
VariableElement Represents a field, enum constant, method or constructor parameter, local variable, class member variable, or exception parameter
ExecutableElement A method, constructor, or initializer (static or instance) that represents a class or interface, including annotation type elements
TypeParameterElement A generic parameter representing a generic class, interface, method, or constructor element

To clarify this, take an example:

package com.xm.test;    // Package name, PackageElement

public class Test< // Class name,TypeElement
    T// Generic parameters,TypeParameterElement
    > {     

    private int a;       // Member variable, VariableElement
    private Test other;  // Member variable, VariableElement

    public Test (a) {}    // Member method, ExecuteableElement
    public void setA // Member method, ExecuteableElementintNewA // method argument, VariableElement) {
        String test;     // Local variable, VariableElement}}Copy the code

When the compiler operates on elements in a Java file, it does so through these classes. That is, when we want to generate Java files from JavaPoet, we can use these subclasses to express elements of a structured program. Any Element object can be forcibly subclassed as necessary. The Element class, on the other hand, is actually an interface that defines a set of methods. Let’s take a look.

public interface Element extends AnnotatedConstruct {  
    /** * Returns the type defined by this element * for example, for the generic Clazz

element, returns the parameterized type Clazz

*/

TypeMirror asType(a); /** * Returns the type of this element: package, class, interface, method, field... * PACKAGE, ENUM, CLASS, ANNOTATION_TYPE, INTERFACE, ENUM_CONSTANT, FIELD, PARAMETER, LOCAL_VARIABLE, EXCEPTION_PARAMETER, * METHOD, CONSTRUCTOR, STATIC_INIT, INSTANCE_INIT, TYPE_PARAMETER, OTHER, RESOURCE_VARIABLE; * / ElementKind getKind(a); PUBLIC, PROTECTED, PRIVATE, ABSTRACT, DEFAULT, STATIC, FINAL, TRANSIENT, VOLATILE, SYNCHRONIZED, NATIVE, STRICTFP; * / Set<Modifier> getModifiers(a); /** * returns a simple name for this element, for example, java.util.Set is simply "Set"; * If this element represents an unspecified package, an empty name is returned; * If it represents a constructor, return the name " "; * If it represents a static initializer, return the name " "; * Returns an empty name */ if it represents an anonymous class or instance initializer Name getSimpleName(a); /** * returns the innermost element enclosing this element. Return the enclosing element if the declaration of this element is wrapped directly in the declaration of another element; * If this element is of top-level type, its package is returned; * If this element is a package, null is returned; * If this element is a generic parameter, null. */ is returned Element getEnclosingElement(a); /** * returns the child wrapped directly by this element */ List<? extends Element> getEnclosedElements(); boolean equals(Object var1); int hashCode(a); To get inherited annotations, use getAllAnnotationMirrors */ List<? extends AnnotationMirror> getAnnotationMirrors(); /** * returns an annotation for this element for the specified type (if one exists), or null otherwise. Annotations can be inherited or can be */ directly existing on the element <A extends Annotation> A getAnnotation(Class<A> annotationType); <R, P> R accept(ElementVisitor<R, P> var1, P var2); } Copy the code

Poet’s Brain -APT(Annotation Processor Tool) Annotation Processor

APT is called the poet’s brain because the core of our entire code-generation task requires methods and portals provided by the annotated processor. In the core part of the whole process, APT is implemented and controlled. APT is a tool for annotation processing, specifically, it is a javAC tool, which is used to scan and process annotations at compile time. An annotation processor takes Java code (or compiled bytecode) as input and generates. Java files as output. The core is given to the processor defined by itself to process. In fact, APT sets aside a set of template interfaces for programming at compile time. By implementing methods in the processor, we can write our own annotation processing flow.

Core – AbstractProcessor APT

Each custom annotation processor inherits the Virtual processor AbstractProcessor to implement its key methods.

AbstractProcessor AbstractProcessor has several key methods:

public class MyProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment env){}@Override
    public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) {}@Override
    public Set<String> getSupportedAnnotationTypes(a) {}@Override
    public SourceVersion getSupportedSourceVersion(a) {}}Copy the code

These are the methods that must be implemented when implementing a custom annotation handler. Here are some highlights of these four methods:

  • Init (ProcessingEnvironment env) : Each annotation handler class must have an empty constructor. However, there is a special init() method that is called by the annotation processing tool with the ProcessingEnviroment parameter. ProcessingEnviroment provides a number of useful utility classes such as Elements, Types and Filer.

  • process(Set
    Annotations, RoundEnvironment env) : This corresponds to the main function of each processor, main(). This is where you write your code to scan, evaluate, and process annotations, as well as generate Java files. Enter the parameter RoundEnviroment, which allows you to query for annotated elements that contain specific annotations. Annotations this is a Boolean value that indicates whether or not the set of annotations have been configured by the processor. It usually returns false when an exception occurs and true when the processing is complete.

  • GetSupportedAnnotationTypes () : must be implementation; Used to indicate which annotation handler is registered for. The return value is a collection of strings containing the legal full name of the annotation type that this processor wants to process.

  • GetSupportedSourceVersion () : used to specify the Java version you use. Usually here return SourceVersion. LatestSupported (), you can also use SourceVersion_RELEASE_6, 7, 8 registered processor version.

Since annotation handlers are javac tools, we must register our custom handlers with Javac by providing a.jar file, packaging your annotation handlers into this file, and inside the JAR, Need to pack a specific file javax.mail annotation. Processing. The Processor to the meta-inf/services directory. And all this is extremely tedious. Fortunately, Google has developed an AutoService annotation for us. All you need to do is import this dependency and annotate your processor class:

@AutoService(Processor.class)
Copy the code

We can then automatically generate files and package them into jars. It saves a lot of trouble.

Now that we’ve covered the processor, let’s take a look at what other tools APT provides.

APT provides four accessibility tools

These four tools are available in an AbstractProcessor implementation class through ProcessingEnvironment:

    private Filer mFiler;
    private Elements mElementUtils;
    private Messager mMessager;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mElementUtils = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
        mFiler = processingEnv.getFiler();
    }
Copy the code

Filer

As the name suggests, file-related operations will be used. Java files are typically generated in conjunction with JavaPoet

Messager

It provides the annotation handler with a way to report error and warning messages. When our custom annotation processor runs incorrectly, the JVM running the annotation processor crashes, printing logs that are difficult for application developers to read. In this case, we can use Messager to output some debugging information, in a more intuitive way to prompt the program to run the error.

Types

Types is a tool for manipulating TypeMirror. TypeMirror is an object in Element obtained by the adType() method. It holds the details of the Element, for example, if Element is a class, its member details are stored in a TypeMirror.

Elements

Elements is a tool for working with Elements. I won’t go into all the details here. I’ll mention it when I use it.

The poet’s toolbox

JavaPoet provides a convenient way to generate Java files from annotations at compile time by manipulating Java file structure elements. So how do you generate it? It’s worth taking a look at what JavaPoet has to offer.

JavaPoet provides us with four common classes for expressing Java file elements

These classes represent elements in Java files in a similar way to elements mentioned above. It doesn’t matter if you don’t understand it right now. I’ll give you an example.

The name of the class meaning
MethodSpec Represents a constructor or method declaration
TypeSpec Represents a class, interface, or enumeration declaration
FieldSpec Represents a member variable, a field declaration
ParameterSpec Represents a parameter that can be used to generate parameters
AnnotationSpec Stand for a note
JavaFile A Java file containing a top-level class

“Poet” in action

Let’s look at a simple example as a whole and then expand to a variety of situations.

A case in point

Suppose we define the following annotation, using what we learned in the previous article:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Xnpe {
    String value(a);
}
Copy the code

Next implement the annotation handler:

@AutoService(Processor.class)
public class XnpeProcess extends AbstractProcessor {

    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement element : annotations) {
            if (element.getQualifiedName().toString().equals(Xnpe.class.getCanonicalName())) {
                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();

                try {
                    JavaFile javaFile = JavaFile.builder("com.xm", helloWorld)
                            .addFileComment(" This codes are generated automatically. Do not modify!")
                            .build();
                    javaFile.writeTo(filer);
                } catch(IOException e) { e.printStackTrace(); }}}return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes(a) {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(Xnpe.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion(a) {
        returnSourceVersion.latestSupported(); }}Copy the code

Now, this is going to take a little bit of patience, because it looks like a lot at first glance, but it’s actually quite simple. Here we summarize a few key steps for implementing a custom annotation handler:

  1. Add the @AutoService annotation to the annotation handler. The @ AutoService (Processor. Java)
  2. Implementation of said custom annotations to the commonly used four methods, namely the init (), the process (), getSupportedAnnotationTypes and getSupportedSourceVersion.
  3. Write logic to handle annotations.

In this example, we will focus on the second, the implementation of the four big methods. The focus is on the processing method, the process() method. Let’s take the core part of it and explain it.

MethodSpec main = MethodSpec.methodBuilder("main") MethodSpec is, of course, a methodBuilder, which creates methods.
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)// Add the qualifier
    .returns(void.class)                           // Specify the return value type
    .addParameter(String[].class, "args")          // Specify the method parameters
    .addStatement("$T.out.println($S)", System.class, "Hello, I am Poet!")// Add logical code.
    .build();                                      / / create
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") / / TypeSpec build a Class
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL) // Add the qualifier
    .addMethod(main)                               // Add the main method you just created to the class.
    .build();                                      / / create
Copy the code

Is the process easy to understand. MethodSpec is used to generate methods that detail annotations on participating code.

If you are careful, you may have noticed something like $T in the code. What is this? Here we go through a few small examples, on the one hand to understand some of the Poet placeholders, on the other hand to familiarize ourselves with the common methods.

Commonly used method

addCodewithaddStatementTo add code
MethodSpec main = MethodSpec.methodBuilder("main")
    .addCode(""
        + "int total = 0; \n"
        + "for (int i = 0; i < 10; i++) {\n"
        + "  total += i;\n"
        + "}\n")
    .build();
Copy the code

The generated is

void main() {
  int total = 0;
  for(int i = 0; i < 10; i++) { total += i; }}Copy the code
  • addCodeUsed to add minimalist code. That is, the code contains only pure Java base types and operations.
  • addStatementUse to add some code that requires import methods. as.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!" )You need to use.addStatementTo declare.
BeginControlFlow and endControlFlow, flow control methods

The flow control method is mainly used to add some flow control code, which looks a little more beautiful than the add method above. For example, the above code can be rewritten as:

MethodSpec main = MethodSpec.methodBuilder("main")
    .addStatement("int total = 0")
    .beginControlFlow("for (int i = 0; i < 10; i++)")
    .addStatement("total += i")
    .endControlFlow()
    .build();
Copy the code

A placeholder

$L Literals

private MethodSpec computeRange(String name, int from, int to, String op) {
  return MethodSpec.methodBuilder(name)
      .returns(int.class)
      .addStatement("int result = 0")
      .beginControlFlow("for (int i = $L; i < $L; i++)", from, to)
      .addStatement("result = result $L i", op)
      .endControlFlow()
      .addStatement("return result")
      .build();
}
Copy the code

When we pass the call, coputeRange(“test”, 0, 10, “+”) generates the following code:

int test(a){
    int result = 0;
    for(int i = 0; i < 10; i++) {
        result = result + i;
    }
    return result;
}
Copy the code

$S String Constant (String)

This one is easier to understand, so I won’t go into it here.

$T type (Types)

MethodSpec today = MethodSpec.methodBuilder("today")
    .returns(Date.class)
    .addStatement("return new $T()", Date.class)
    .build(); // Create the today method
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    .addMethod(today)
    .build(); Create the HelloWorld class
JavaFile javaFile = JavaFile.builder("com.xm.helloworld", helloWorld).build();
javaFile.writeTo(System.out);// Write a Java file
Copy the code

The generated code is shown below, which, as we can see, automatically imports the required packages. This is one of the benefits of using placeholders, and one of the benefits of using JavaPoet.

package com.xm.helloworld;

import java.util.Date;

public final class HelloWorld {
  Date today(a) {
    return newDate(); }}Copy the code

What if we want to import our own class? Get (” class path “, “class name”), combined with $T

ClassName testClass = ClassName.get("com.xm"."TestClass");
ClassName list = ClassName.get("java.util"."List");
ClassName arrayList = ClassName.get("java.util"."ArrayList");
TypeName listOftestClasses = ParameterizedTypeName.get(list, testClass);

MethodSpec xNpe = MethodSpec.methodBuilder("xNpe")
    .returns(listOftestClasses)
    .addStatement("$T result = new $T<>()", listOftestClasses, arrayList)
    .addStatement("result.add(new $T())", testClass)
    .addStatement("result.add(new $T())", testClass)
    .addStatement("result.add(new $T())", testClass)
    .addStatement("return result")
    .build();
Copy the code

The generated code looks like this:

package com.xm.helloworld;

import com.xm.TestClass;
import java.util.ArrayList;
import java.util.List;

public final class HelloWorld {
  List<TestClass> xNpe(a) {
    List<TestClass> result = new ArrayList<>();
    result.add(new TestClass());
    result.add(new TestClass());
    result.add(new TestClass());
    returnresult; }}Copy the code

Javapoet also supports import static, which is added by addStaticImport:

JavaFile.builder("com.xm.helloworld", hello)
    .addStaticImport(TestClass, "START")
    .addStaticImport(TestClass2, "*")
    .addStaticImport(Collections.class, "*")
    .build();
Copy the code

$N named (Names)

This usually refers to method names or variable names that we generate ourselves. For example:

public String byteToHex(int b) {
  char[] result = new char[2];
  result[0] = hexDigit((b >>> 4) & 0xf);
  result[1] = hexDigit(b & 0xf);
  return new String(result);
}

public char hexDigit(int i) {
  return (char) (i < 10 ? i + '0' : i - 10 + 'a');
}
Copy the code

In this example, we need to call the hexDigit method in byteToHex, so we can use $N to represent this reference. This call statement is then generated by passing the method name.

MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit")
    .addParameter(int.class, "i")
    .returns(char.class)
    .addStatement("return (char) (i < 10 ? i + '0' : i - 10 + 'a')")
    .build();

MethodSpec byteToHex = MethodSpec.methodBuilder("byteToHex")
    .addParameter(int.class, "b")
    .returns(String.class)
    .addStatement("char[] result = new char[2]")
    .addStatement("result[0] = $N((b >>> 4) & 0xf)", hexDigit)
    .addStatement("result[1] = $N(b & 0xf)", hexDigit)
    .addStatement("return new String(result)")
    .build();
Copy the code

Build the elements of the Java class from the top down

Common methods

When we define a method, we should also add some modifiers to the method, such as Modifier. You can use the addModifiers() method to:

MethodSpec test = MethodSpec.methodBuilder("test")
    .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED)
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addMethod(test)
    .build();
Copy the code

The following code is generated:

public abstract class HelloWorld {
  protected abstract void test(a);
}
Copy the code

The constructor

The constructor is just a special method, so you can use MethodSpec to generate the constructor method. Use constrctorBuilder to generate:

MethodSpec flux = MethodSpec.constructorBuilder()
    .addModifiers(Modifier.PUBLIC)
    .addParameter(String.class, "greeting")
    .addStatement("this.$N = $N"."greeting"."greeting")
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL)
    .addMethod(flux)
    .build();
Copy the code

The code will be generated:

public class HelloWorld {
  private final String greeting;

  public HelloWorld(String greeting) {
    this.greeting = greeting; }}Copy the code

Various parameters

ParameterSpec also has its own special ParameterSpec class. You can use parameterSpec.Builder () to generate the parameter, which is then used in MethodSpec’s addParameter to make it more elegant.

ParameterSpec android = ParameterSpec.builder(String.class, "android")
    .addModifiers(Modifier.FINAL)
    .build();

MethodSpec welcomeOverlords = MethodSpec.methodBuilder("test")
    .addParameter(android)
    .addParameter(String.class, "robot", Modifier.FINAL)
    .build();
Copy the code

Generated code

void test(final String android, final String robot) {}Copy the code

Fields, member variables

You can use FieldSpec to declare a field and add it to a class:

FieldSpec android = FieldSpec.builder(String.class, "android")
    .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(android)
    .addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL)
    .build();
Copy the code

Generated code:

public class HelloWorld {
  private final String android;
  private final String robot;
}
Copy the code

The Builder can create fields in more detail, such as Javadoc, Annotations, or initialize field parameters, for example:

FieldSpec android = FieldSpec.builder(String.class, "android")
    .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
    .initializer("$S + $L"."Pie v.".9.0)// Initialize the assignment
    .build();
Copy the code

Corresponding generated code:

private final String android = "Pie v." + 9.0;
Copy the code

interface

Interface method must be PUBLIC and the ABSTRACT interface field must be a PUBLIC STATIC FINAL, using TypeSpec. InterfaceBuilder is as follows:

TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(FieldSpec.builder(String.class, "KEY_START")
        .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
        .initializer("$S"."start")
        .build())
    .addMethod(MethodSpec.methodBuilder("beep")
        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
        .build())
    .build();
Copy the code

The generated code looks like this:

public interface HelloWorld {
  String KEY_START = "start";
  void beep(a);
}
Copy the code

Enumerated type

Use TypeSpec. EnumBuilder to create and addEnumConstant to add enumeration values:

TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
    .addModifiers(Modifier.PUBLIC)
    .addEnumConstant("ROCK")
    .addEnumConstant("SCISSORS")
    .addEnumConstant("PAPER")
    .build();
Copy the code

Generated code

public enum Roshambo {
  ROCK,
  SCISSORS,
  PAPER
}
Copy the code

Anonymous inner class

You need to use type.anonymousinnerClass (“”), which can usually be referred to using the $L placeholder:

TypeSpec comparator = TypeSpec.anonymousClassBuilder("")
    .addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class))
    .addMethod(MethodSpec.methodBuilder("compare")
        .addAnnotation(Override.class)
        .addModifiers(Modifier.PUBLIC)
        .addParameter(String.class, "a")
        .addParameter(String.class, "b")
        .returns(int.class)
        .addStatement("return $N.length() - $N.length()"."a"."b")
        .build())
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addMethod(MethodSpec.methodBuilder("sortByLength")
        .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings")
        .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator)
        .build())
    .build();
Copy the code

Generated code:

void sortByLength(List<String> strings) {
  Collections.sort(strings, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
      returna.length() - b.length(); }}); }Copy the code

A particularly tricky problem in defining anonymous inner classes is the construction of parameters. In the code above we passed an empty string with no arguments. TypeSpec. AnonymousClassBuilder (” “).

annotations

Annotations are relatively simple to use. AddAnnotation lets you add:

MethodSpec toString = MethodSpec.methodBuilder("toString")
    .addAnnotation(Override.class)
    .returns(String.class)
    .addModifiers(Modifier.PUBLIC)
    .addStatement("return $S"."Hello XiaoMing")
    .build();
Copy the code

The generated code

@Override
public String toString(a) {
  return "Hello XiaoMing";
}
Copy the code

Annotationspec.builder () allows you to set attributes for annotations:

MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(Headers.class)
        .addMember("accept"."$S"."application/json; charset=utf-8")
        .addMember("userAgent"."$S"."Square Cash")
        .build())
    .addParameter(LogRecord.class, "logRecord")
    .returns(LogReceipt.class)
    .build();
Copy the code

The code generation is as follows

@Headers(
    accept = "application/json; charset=utf-8",
    userAgent = "Square Cash"
)
LogReceipt recordEvent(LogRecord logRecord);
Copy the code
MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(HeaderList.class)
        .addMember("value"."$L", AnnotationSpec.builder(Header.class)
            .addMember("name"."$S"."Accept")
            .addMember("value"."$S"."application/json; charset=utf-8")
            .build())
        .addMember("value"."$L", AnnotationSpec.builder(Header.class)
            .addMember("name"."$S"."User-Agent")
            .addMember("value"."$S"."Square Cash")
            .build())
        .build())
    .addParameter(LogRecord.class, "logRecord")
    .returns(LogReceipt.class
    .build
Copy the code

Generating a Java file

To generate a Java file, we need to use the Filer and Elements mentioned above. Note the following code, important is the package name, class name specified. The file names generated here generally follow some convention so that reflection code is written in advance.

// Get the package name of the file to be generated
public String getPackageName(TypeElement type) {
    return mElementUtils.getPackageOf(type).getQualifiedName().toString();
}

// Get the class name of the file to be generated
private static String getClassName(TypeElement type, String packageName) {
    int packageLen = packageName.length() + 1;
    return type.getQualifiedName().toString().substring(packageLen).replace('. '.'$');
}

// Generate the file
private void writeJavaFile(a) {
    String packageName = getPackageName(mClassElement);
    String className = getClassName(mClassElement, packageName);
    ClassName bindClassName = ClassName.get(packageName, className);
    TypeSpec finderClass = TypeSpec.classBuilder(bindClassName.simpleName() + "? Injector")
        .addModifiers(Modifier.PUBLIC)
        .addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJECTOR,
                                                     TypeName.get(mClassElement.asType())))
        .addMethod(methodBuilder.build())
        .build();
    // Use JavaFile builder to generate Java files
    JavaFile.builder(packageName, finderClass).build().writeTo(mFiler);
}
Copy the code

conclusion

Over the course of the two articles, we have become familiar with the use of Java annotations, how to write them, and how to use them to serve our code or programs. This article lists many specific examples that I hope will cover all aspects of your daily use. You can also bookmark this article for reference when using JavaPoet.

Xiaoming produced, must be a boutique

Welcome to pay attention to xNPE technology forum, more original dry goods daily push.