Java代码生成工具-Javapoet

507 阅读2分钟

JavaPoet介绍

JavaPoet 是 Square 公司开发的一个开源 Java 库,用于通过 Java 代码生成器来创建和修改 Java 代码。它的目标是通过编程的方式生成 Java 代码,以简化代码生成的过程,避免手动拼接字符串等繁琐的操作。JavaPoet 可以用于生成类、方法、字段、注解等各种 Java 代码结构。

JavaPoet特点:

  1. 类型安全: JavaPoet 使用了类型安全的 API,避免了在生成代码时出现类型错误。
  2. 易于使用: JavaPoet 提供了简洁的 API,使得生成代码变得直观且易于理解。
  3. 代码清晰: 生成的代码具有良好的可读性,不需要手动处理字符串拼接。
  4. 支持链式调用: JavaPoet 的 API 支持链式调用,可以方便地组合多个操作。

源文件元素

TypeSpecParameterSpecMethodSpecCodeBlockJavaFile都是JavaPoet提供的用于描述一个源文件元素的类。

  • TypeSpec代表了一个接口、类、注解、枚举的定义。你可以使用它来定义类的修饰符、字段、方法等。
    // 类生成:
    TypeSpec.classBuilder("HelloWorld")
    
    //接口:
    TypeSpec.interfaceBuilder("HelloWorld")
    
  • ParameterSpec代表一个成员变量、函数参数的定义。它包含参数的类型、名称和修饰符等信息。
    ParameterSpec parameterSpec = ParameterSpec.builder(String.class, "message")
        .addModifiers(Modifier.FINAL)
        .build();
    
  • MethodSpec代表了方法的定义。你可以使用它来定义方法的修饰符、返回类型、参数、代码块等。
    MethodSpec methodSpec = MethodSpec.methodBuilder("greet")
    .addModifiers(Modifier.PUBLIC)
    .returns(void.class)
    .addParameter(parameterSpec)
    .addStatement("$T.out.println($S + $N)", System.class, "Message: ", "message")
    .build();
    
    
  • CodelBlock 用于描述一段代码块。你可以在其中添加多个语句、变量、表达式等。
    CodeBlock codeBlock = CodeBlock.builder()
        .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
        .addStatement("int sum = $L + $L", 5, 3)
        .build();
    
  • JavaFile表示源文件本身。一个java文件正式通过以上几种类型的嵌套、组合,最终描述成一个完整的java源文件。
    JavaFile javaFile = JavaFile.builder("com.example", helloWorld) .addStaticImport(System.class, "out") .build();
    

通过这些 API,我们可以以编程方式构建 Java 代码元素,并将它们组合成类、方法等结构。最后,可以使用 JavaFile 将生成的代码写入文件中。

JavaPoet占位符

JavaPoet 中,这些符号 $L$S$T$N 等被称为代码占位符,它们用于在生成代码时插入不同类型的值。下面是这些代码占位符的含义和用法:

符号含义示例
$L字面量.addStatement("int x = $L", 42)
$S字符串.addStatement("$T.out.println($S)", System.class, "Hello")
$T类型.addStatement("$T value = $L", String.class, "hello")
$N名称(变量、方法等).addStatement("int $N = 0", "count")

这些代码占位符允许你在生成代码时将变量、字符串、类型等值插入到代码中,而不需要手动拼接字符串,从而避免了一些常见的错误和繁琐的操作。

例如,在 .addStatement("$T.out.println($S)", System.class, "Hello") 这个代码中,$T 表示类型(System.class),$S 表示字符串("Hello")。

Javapoet示例

引入依赖

<dependency>
    <groupId>com.squareup</groupId>
    <artifactId>javapoet</artifactId>
    <version>1.13.0</version>
</dependency>

生成一个main方法,输出‘Hello, JavaPoet!’


public static void main(String[] args) throws IOException {
    // 创建一个方法
    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();

    // 创建一个 Java 文件
    JavaFile javaFile = JavaFile.*builder*("com.example.helloworld", helloWorld)
            .build();

    // 将生成的代码写入文件
    javaFile.writeTo(System.*out*);

}

生成代码如下:

package com.example.helloworld;
import java.lang.String;
import java.lang.System;

public final class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, JavaPoet!");
  }
}

静态方法引入示例

JavaPoet支持静态导入。它通过显式收集类型成员名来实现。

ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards");

MethodSpec beyond = MethodSpec.methodBuilder("beyond")
    .returns(listOfHoverboards)
    .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList)
    .addStatement("result.add($T.createNimbus(2000))", hoverboard)
    .addStatement("result.add($T.createNimbus(\"2001\"))", hoverboard)
    .addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", hoverboard, namedBoards)
    .addStatement("$T.sort(result)", Collections.class)
    .addStatement("return result.isEmpty() ? $T.emptyList() : result", Collections.class)
    .build();

TypeSpec hello = TypeSpec.classBuilder("HelloWorld")
    .addMethod(beyond)
    .build();

JavaFile.builder("com.example.helloworld", hello)
    .addStaticImport(hoverboard, "createNimbus")
    .addStaticImport(namedBoards, "*")
    .addStaticImport(Collections.class, "*")
    .build();

输出结果如下:

package com.example.helloworld;

import static com.mattel.Hoverboard.Boards.*;
import static com.mattel.Hoverboard.createNimbus;
import static java.util.Collections.*;

import com.mattel.Hoverboard;
import java.util.ArrayList;
import java.util.List;

class HelloWorld {
  List<Hoverboard> beyond() {
    List<Hoverboard> result = new ArrayList<>();
    result.add(createNimbus(2000));
    result.add(createNimbus("2001"));
    result.add(createNimbus(THUNDERBOLT));
    sort(result);
    return result.isEmpty() ? emptyList() : result;
  }
}

更多示例可以参考官方文档。