Java Code Generation Framework - JavaPoet

introduce

      JavaPoet is a Java API for generating .Java source files.

         Source file generation can be very useful when dealing with things like annotation processing or interacting with metadata files (eg database schemas, protocol formats). By generating code, you eliminate the need to write boilerplate while also maintaining a single source of metadata.


integrated into the project

    
<!-- https://mvnrepository.com/artifact/com.squareup/javapoet -->
        <dependency>
            <groupId>com.squareup</groupId>
            <artifactId>javapoet</artifactId>
            <version>1.11.0</version>
        </dependency>


code generation

    kind

           
        //generate class
        TypeSpec helloWorld=TypeSpec.classBuilder("HelloWorld")
                .build();
        //Generate Java file
        JavaFile javaFile=JavaFile.builder("com.itcast.lyc",helloWorld).build();

        try {
            //Write the contents of the file to the window and print it out
            javaFile.writeTo(System.out);
        } catch (IOException e) {
            e.printStackTrace ();
        }

     The generated content is as follows:

package com.itcast.lyc;

class HelloWorld {
}

     method

           common method

        //generate class constructor
        TypeSpec.Builder helloWorldBuilder=TypeSpec.classBuilder("HelloWorld");

        MethodSpec main=MethodSpec.methodBuilder("main")//The constructor of the method
                .addParameter(String[].class,"args")//Add parameters
                .returns(void.class)//Add return value
                .addStatement("$T.out.println($S)",System.class,"helloWorld")//添加内容
                .addStatement("$T.out.println($L)",System.class,100L)//Display characters
                .build();

        TypeSpec helloWorld=helloWorldBuilder.addMethod(main).build();

        //Generate Java file
        JavaFile javaFile=JavaFile.builder("com.itcast.lyc",helloWorld).build();

        try {
            //Write the contents of the file to the window and print it out
            javaFile.writeTo(System.out);
        } catch (IOException e) {
            e.printStackTrace ();
        }

Among them, $T represents the .class class of the class. Using $T and adddStatement matching can automatically import the package. $S represents the injection string. AdddStatement is a syntax of the injected string format, which will automatically add semicolons and newlines to the statement. There is also $L for numeric types.

The generated code is as follows:

package com.itcast.lyc;

import java.lang.String;
import java.lang.System;

class HelloWorld {
  void main(String[] args) {
    System.out.println("helloWorld");
    System.out.println(100);
  }
}

There are also .addCode("asdasds;\n") methods to add code , but .addCode will not automatically generate semicolons and newlines, so you don't need to, you can also write .addCode(CodeBlock.builder().addStatement(" $S","|sdsad").build()) .

        control flow

    JavaPoet provides us with a convenient API for generating control flow. For example, we want to generate the following:

if (true){
   System.out.println("ok");
}

It's hard for us to imagine the trouble of printing the {} brackets ourselves, so JavaPoet provides beginControlFlow and endControlFlow in the method constructor.

  MethodSpec main=MethodSpec.methodBuilder("main")//The constructor of the method
                .addParameter(String[].class,"args")//Add parameters
                .returns(void.class)//Add return value
                .addStatement("$T.out.println($S)",System.class,"helloWorld")//添加内容
                .addStatement("$T.out.println($L)",System.class,100L)//Display characters
                .beginControlFlow("if(true)")//Control flow begins
                .addStatement("$T.out.println($S)",System.class,"ok")
                .endControlFlow()//End of control flow
                .build();

abstract class

MethodSpec abstractMethon=MethodSpec.methodBuilder("testAbs")
                .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                .build();

        TypeSpec typeSpec=TypeSpec.classBuilder("TestAbs")
                .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                .addMethod(abstractMethon)
                .build();

interface class

   MethodSpec abstractMethon=MethodSpec.methodBuilder("testInterfaces")
                .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                .build();

        TypeSpec typeSpec=TypeSpec.interfaceBuilder("TestInterfaces")
                .addModifiers(Modifier.PUBLIC)
                .addField(FieldSpec.builder(String.class,"ONLY_ONCE")
                          .addModifiers(Modifier.PUBLIC,Modifier.STATIC,Modifier.FINAL)
                          .initializer("$S","OKKK")
                          .build())
                //add field
                .addMethod(abstractMethon)
                .build();

 Construction method

      //Construction method
        MethodSpec consructorMethod=MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(Integer.class,"age")//Parameter
                .addStatement("this.$N=$N","age","age")//Add style code $N represents a reference to the current class
                .build();

        TypeSpec helloWorld= helloWorldBuilder
                .addMethod(main)//Add main method
                .addField(FieldSpec.builder(Integer.class,"age").addModifiers(Modifier.PRIVATE).build())//添加字段
                .addMethod(consructorMethod)//Add constructor
                .build();

Add parameters

     
.addParameter(Integer.class,"age")//Parameter
  
ParameterizedTypeName parameterizedTypeNameMap=ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(Integer.class),
                ParameterizedTypeName.get(
                        ClassName.get(Class.class),
                        WildcardTypeName.subtypeOf(ClassName.get(Object.class))
                )
        );

        ParameterizedTypeName parameterizedTypeNameList=ParameterizedTypeName.get(
                ClassName.get(List.class),
                WildcardTypeName.subtypeOf(ClassName.get(Integer.class))
        );

        //Add a method with parameters
        MethodSpec parmMethod=MethodSpec.methodBuilder("welomeBeiJing")
                .addParameter(String.class,"parm")
                .addParameter(ParameterSpec.builder(parameterizedTypeNameMap,"map").build())
                .addParameter(ParameterSpec.builder(parameterizedTypeNameList,"list").build())
                .build();
The final result is:
void welomeBeiJing(String parm, Map<Integer, Class<?>> map, List<? extends Integer> list) {
  }
    Parameters that need to generate wildcards use 

.addParameter(ParameterSpec.builder(TypeVariableName.get("T"),"t").build())
The following quotes from others to deepen understanding: Click to open the link



add field

.addField(FieldSpec.builder(Integer.class,"age").addModifiers(Modifier.PRIVATE).build())//添加字段

Enums

File file=new File(System.getProperty("user.dir"),"\\src\\main\\java");


        TypeSpec typeSpec=TypeSpec.enumBuilder("EnumsTest")
                .addModifiers(Modifier.PUBLIC)
                .addEnumConstant("Rock",TypeSpec.anonymousClassBuilder("$S","Page")
                            .addMethod(MethodSpec.methodBuilder("toString")
                                    .addAnnotation (Override.class)
                                    .addModifiers(Modifier.PUBLIC)
                                    .addStatement("return $S","avalanche")
                                    .returns(String.class)
                                    .build())
                        .build())
                .addEnumConstant("Sci",TypeSpec.anonymousClassBuilder("$S","ssss").build())
                .addEnumConstant("Apple",TypeSpec.anonymousClassBuilder("$S","sad").build())
                .addField(String.class,"hand",Modifier.PRIVATE,Modifier.FINAL)
                .addMethod(MethodSpec.constructorBuilder()
                          .addParameter(String.class,"hand")
                          .addStatement("this.$N=$N","hand","hand").build())
                .build();

        JavaFile javaFile=JavaFile.builder("com.itcast.lyc.javapoet",typeSpec).build();

        javaFile.writeTo(file);
  Generate code:
package com.itcast.lyc.javapoet;

import java.lang.Override;
import java.lang.String;

public enum EnumsTest {
  Rock("Page") {
    @Override
    public String toString() {
      return "avalanche";
    }
  },

  Sci("ssss"),

  Apple("sad");

  private final String hand;

  EnumsTest(String hand) {
    this.hand=hand;
  }
}
Enumeration types are added through the TypeSpec.enumBuilder constructor.

inner class

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();

get java code

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

The inner class constructor initializes the inner class through the inner class constructor TypeSpec.anonymousClassBuilder(""), and then uses $L to refer to the corresponding inner class.


add notes

    Fields, methods, and classes can be annotated

MethodSpec dismiss = MethodSpec.methodBuilder("dismiss")
    .addJavadoc("Hides {@code message} from the caller's history. Other\n"
        + "participants in the conversation will continue to see the\n"
        + "message in their own history unless they also delete it.\n")
    .addJavadoc("\n")
    .addJavadoc("<p>Use {@link #delete($T)} to delete the entire\n"
        + "conversation for all participants.\n", Conversation.class)
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addParameter(Message.class, "message")
    .build();

Effect

/**
   * Hides {@code message} from the caller's history. Other
   * participants in the conversation will continue to see the
   * message in their own history unless they also delete it.
   *
   * <p>Use {@link #delete(Conversation)} to delete the entire
   * conversation for all participants.
   */
  void dismiss(Message message);

Use $T to refer to classes.


add annotation

Use AnnotationSpec.builder() to add 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();

Effect

@Headers(
    accept = "application/json; charset=utf-8",
    userAgent = "Square Cash"
)
LogReceipt recordEvent(LogRecord logRecord);

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325480398&siteId=291194637