Annotation Processor

Lombok provides simple annotations that can be used to generate Javabeans and getter/setter methods, improving development efficiency and saving development time. Today we’ll take a look at what Lombok uses to do this. Lombok actually uses the annotation processor, a new feature added in jdk1.5. Like @getter, which is just an annotation, the real processing is done inside the annotation handler. Official reference link.

background

The Pluggable Annotation Processing API (Pluggable Annotation Processing API) is the Pluggable Annotation Processing API (Pluggable Annotation Processing API). It is an implementation of the JSR269 proposal. How does it work? Please refer to the following figure:





practice

So if you look at the data above, you should have a general idea in your head, and now let’s actually do it and write a simple example and do it. To use an annotation processor, there are two steps: 1. Define a custom annotation and 2

So let’s do a very simple example, which is to add an @InterfaceAnnotation to a class, and compile it to generate an interface class with an “I”+ class name. First I define two moudles, one for writing annotations and handlers, and one for calling annotations.

Step 1: Define a custom annotation

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface InterfaceAnnotation {
}
Copy the code

1.@Target: what is the annotation used on, where elementtype. TYPE means the annotation is used on a class 2.@Retention: retentionPolicy. SOURCE is the SOURCE code stage, the compiled class does not have this annotation

Step 2: Inherit AbstractProcessor and implement the process method

@SupportedAnnotationTypes(value = {"com.example.processor.InterfaceAnnotation"})
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
public class InterfaceProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, "Entered the InterfaceProcessor ~~~");
        // Find the class with the InterfaceProcessor
        Set<? extends Element> clazz = roundEnv.getElementsAnnotatedWith(InterfaceAnnotation.class);
        clazz.forEach(item -> {
            // Generate an interface class with the class name I +
            String className = item.getSimpleName().toString();
            className = "I" + className.substring(0.1) + className.substring(1);
            TypeSpec typeSpec = TypeSpec.interfaceBuilder(className).addModifiers(Modifier.PUBLIC).build();

            try {
                // Generate a Java file
                JavaFile.builder("com.example.processor", typeSpec).build().writeTo(new File("./src/main/java/"));
            } catch(IOException e) { e.printStackTrace(); }});return true; }}Copy the code

1. @ SupportedAnnotationTypes: it means the processor class effect what to note 2. @ SupportedSourceVersion: support Java version Annotations (@supportedanNotationTypes); roundEnv: Indicates the environment information of the current and last processing phases TypeSpec is a class in javaPoet. JavaPoet is a Java plugin that generates Java files. It’s useful, so we use this class to generate Java files. In fact, there are many ways to generate Java files using Java PrintWriter and other input and output streams. 6.Messager is used to print out information. System.out.println is also used. If true is returned, subsequent annotation processors will not process the annotation. If false, the annotation will be processed by other annotation processors in the next processing round.

Write well, here you need to specify the processor, meta-inf/services/javax.mail annotation. Processing. The processor to write good com. Example. Processor. InterfaceProcessor. If you don’t know what this is, take a look at my other blog post. What is SPI? We are compiling the annotation processor and setting up the plugin in Maven:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <! The processor cannot be found without this sentence.
              <compilerArgument>-proc:none</compilerArgument>
        </configuration>
</plugin>
Copy the code

The directory structure looks like this:

. ├ ─ ─ HELP. Md ├ ─ ─ pom. The XML ├ ─ ─ processor. On iml └ ─ ─ the SRC └ ─ ─ the main ├ ─ ─ Java │ └ ─ ─ com │ └ ─ ─ example │ └ ─ ─ processor │ ├ ─ ─ ├ ─ garbage, ├ ─ garbage, garbage, garbage, garbage, garbage, garbage, garbage, garbage, garbage javax.annotation.processing.ProcessorCopy the code

Then MVN clean install.

Step 3: Use annotations

Before use, the annotation handler must be compiled. The JAR package that introduces the annotation processor. The test class plus the @InterfaceAnnotation

@InterfaceAnnotation
public class TestProcessor {}Copy the code

Maven specifies the annotation handler to use at compile time.

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> The < version > 3.7.0 < / version > < configuration > <source> 1.8 < /source> <target>1.8</target> <encoding>UTF-8</encoding> <annotationProcessors> com.example.processor.InterfaceProcessor </annotationProcessor> </annotationProcessors> </configuration> </plugin>Copy the code

The directory structure is

. ├ ─ ─ HELP. Md ├ ─ ─ pom. The XML ├ ─ ─ the SRC │ └ ─ ─ the main │ ├ ─ ─ Java │ │ └ ─ ─ com │ │ └ ─ ─ example │ │ └ ─ ─test│ ├ ─ ├ ─ sci-imp. Java │ ├ ─ sci-impCopy the code

Then MVN compile, generate Java files, at this time the directory structure is:

. ├ ─ ─ HELP. Md ├ ─ ─ pom. The XML ├ ─ ─ the SRC │ └ ─ ─ the main │ ├ ─ ─ Java │ │ └ ─ ─ com │ │ └ ─ ─ example │ │ ├ ─ ─ processor │ │ │ └ ─ ─ │ │ ├ ─ ├ ─ 08.02.00 (0 folders, 2 filestest│ │ └ ─ ─ TestProcessor. Java │ └ ─ ─ resources ├ ─ ─ target │ ├ ─ ─ classes │ │ └ ─ ─ com │ │ └ ─ ─ example │ │ └ ─ ─test│ │ └ ─ ─ TestProcessor. Class │ ├ ─ ─ generated - sources │ │ └ ─ ─ annotations │ └ ─ ─ maven - status │ └ ─ ─ maven - compiler - plugin │ └ ─ ─ the compile │ └ ─ ─ the default - the compile │ ├ ─ ─ createdFiles. LST │ └ ─ ─ inputFiles. LST └ ─ ─ test. On imlCopy the code

When you see the generated Java file, you’re done

Conclusion:

1. Java annotation processor can be used in many places, such as Lombok, Android generation fragment, etc., using only one annotation can save a lot of code, improve efficiency; 2. This article only lists a very simple example, many annotation processor API is not used, readers can be interested in their own research, and there are apis involving abstract syntax tree; 3. Annotation handlers can be used to generate new classes to perform certain functions, but cannot directly modify the current class.

References:

1. docs.oracle.com/javase/8/do… 2. jcp.org/aboutjava/c… 3. github.com/square/java… 4. www.cnblogs.com/throwable/p… 5. notatube.blogspot.com/2010/11/pro… 6. www.baeldung.com/java-annota… 7. hannesdorfmann.com/annotation-…